@quillsql/react 2.12.36 → 2.12.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (456) hide show
  1. package/dist/cjs/ChartBuilder.js +2 -2
  2. package/dist/cjs/Dashboard.d.ts.map +1 -1
  3. package/dist/cjs/Dashboard.js +1 -1
  4. package/dist/cjs/components/Chart/ChartError.js +1 -1
  5. package/dist/cjs/utils/dataFetcher.d.ts.map +1 -1
  6. package/dist/cjs/utils/dataFetcher.js +7 -1
  7. package/dist/cjs/utils/report.d.ts.map +1 -1
  8. package/dist/cjs/utils/report.js +6 -3
  9. package/package.json +1 -1
  10. package/dist/esm/Chart.d.ts +0 -204
  11. package/dist/esm/Chart.d.ts.map +0 -1
  12. package/dist/esm/Chart.js +0 -431
  13. package/dist/esm/ChartBuilder.d.ts +0 -339
  14. package/dist/esm/ChartBuilder.d.ts.map +0 -1
  15. package/dist/esm/ChartBuilder.js +0 -1312
  16. package/dist/esm/ChartEditor.d.ts +0 -209
  17. package/dist/esm/ChartEditor.d.ts.map +0 -1
  18. package/dist/esm/ChartEditor.js +0 -182
  19. package/dist/esm/Context.d.ts +0 -14
  20. package/dist/esm/Context.d.ts.map +0 -1
  21. package/dist/esm/Context.js +0 -269
  22. package/dist/esm/Dashboard.d.ts +0 -279
  23. package/dist/esm/Dashboard.d.ts.map +0 -1
  24. package/dist/esm/Dashboard.js +0 -660
  25. package/dist/esm/DateRangePicker/Calendar.d.ts +0 -17
  26. package/dist/esm/DateRangePicker/Calendar.d.ts.map +0 -1
  27. package/dist/esm/DateRangePicker/Calendar.js +0 -164
  28. package/dist/esm/DateRangePicker/DateRangePicker.d.ts +0 -39
  29. package/dist/esm/DateRangePicker/DateRangePicker.d.ts.map +0 -1
  30. package/dist/esm/DateRangePicker/DateRangePicker.js +0 -95
  31. package/dist/esm/DateRangePicker/DateRangePickerButton.d.ts +0 -22
  32. package/dist/esm/DateRangePicker/DateRangePickerButton.d.ts.map +0 -1
  33. package/dist/esm/DateRangePicker/DateRangePickerButton.js +0 -134
  34. package/dist/esm/DateRangePicker/QuillDateRangePicker.d.ts +0 -25
  35. package/dist/esm/DateRangePicker/QuillDateRangePicker.d.ts.map +0 -1
  36. package/dist/esm/DateRangePicker/QuillDateRangePicker.js +0 -260
  37. package/dist/esm/DateRangePicker/dateRangePickerUtils.d.ts +0 -80
  38. package/dist/esm/DateRangePicker/dateRangePickerUtils.d.ts.map +0 -1
  39. package/dist/esm/DateRangePicker/dateRangePickerUtils.js +0 -521
  40. package/dist/esm/DateRangePicker/index.d.ts +0 -3
  41. package/dist/esm/DateRangePicker/index.d.ts.map +0 -1
  42. package/dist/esm/DateRangePicker/index.js +0 -2
  43. package/dist/esm/QuillProvider.d.ts +0 -163
  44. package/dist/esm/QuillProvider.d.ts.map +0 -1
  45. package/dist/esm/QuillProvider.js +0 -104
  46. package/dist/esm/ReportBuilder.d.ts +0 -318
  47. package/dist/esm/ReportBuilder.d.ts.map +0 -1
  48. package/dist/esm/ReportBuilder.js +0 -3279
  49. package/dist/esm/SQLEditor.d.ts +0 -272
  50. package/dist/esm/SQLEditor.d.ts.map +0 -1
  51. package/dist/esm/SQLEditor.js +0 -510
  52. package/dist/esm/Table.d.ts +0 -167
  53. package/dist/esm/Table.d.ts.map +0 -1
  54. package/dist/esm/Table.js +0 -215
  55. package/dist/esm/TableChart.d.ts +0 -15
  56. package/dist/esm/TableChart.d.ts.map +0 -1
  57. package/dist/esm/TableChart.js +0 -95
  58. package/dist/esm/assets/ArrowDownHeadIcon.d.ts +0 -5
  59. package/dist/esm/assets/ArrowDownHeadIcon.d.ts.map +0 -1
  60. package/dist/esm/assets/ArrowDownHeadIcon.js +0 -3
  61. package/dist/esm/assets/ArrowDownIcon.d.ts +0 -5
  62. package/dist/esm/assets/ArrowDownIcon.d.ts.map +0 -1
  63. package/dist/esm/assets/ArrowDownIcon.js +0 -3
  64. package/dist/esm/assets/ArrowDownRightIcon.d.ts +0 -5
  65. package/dist/esm/assets/ArrowDownRightIcon.d.ts.map +0 -1
  66. package/dist/esm/assets/ArrowDownRightIcon.js +0 -3
  67. package/dist/esm/assets/ArrowLeftHeadIcon.d.ts +0 -5
  68. package/dist/esm/assets/ArrowLeftHeadIcon.d.ts.map +0 -1
  69. package/dist/esm/assets/ArrowLeftHeadIcon.js +0 -3
  70. package/dist/esm/assets/ArrowRightHeadIcon.d.ts +0 -5
  71. package/dist/esm/assets/ArrowRightHeadIcon.d.ts.map +0 -1
  72. package/dist/esm/assets/ArrowRightHeadIcon.js +0 -3
  73. package/dist/esm/assets/ArrowRightIcon.d.ts +0 -5
  74. package/dist/esm/assets/ArrowRightIcon.d.ts.map +0 -1
  75. package/dist/esm/assets/ArrowRightIcon.js +0 -3
  76. package/dist/esm/assets/ArrowUpHeadIcon.d.ts +0 -5
  77. package/dist/esm/assets/ArrowUpHeadIcon.d.ts.map +0 -1
  78. package/dist/esm/assets/ArrowUpHeadIcon.js +0 -3
  79. package/dist/esm/assets/ArrowUpIcon.d.ts +0 -5
  80. package/dist/esm/assets/ArrowUpIcon.d.ts.map +0 -1
  81. package/dist/esm/assets/ArrowUpIcon.js +0 -3
  82. package/dist/esm/assets/ArrowUpRightIcon.d.ts +0 -5
  83. package/dist/esm/assets/ArrowUpRightIcon.d.ts.map +0 -1
  84. package/dist/esm/assets/ArrowUpRightIcon.js +0 -3
  85. package/dist/esm/assets/CalendarIcon.d.ts +0 -5
  86. package/dist/esm/assets/CalendarIcon.d.ts.map +0 -1
  87. package/dist/esm/assets/CalendarIcon.js +0 -3
  88. package/dist/esm/assets/CalendarNormalIcon.d.ts +0 -5
  89. package/dist/esm/assets/CalendarNormalIcon.d.ts.map +0 -1
  90. package/dist/esm/assets/CalendarNormalIcon.js +0 -3
  91. package/dist/esm/assets/DoubleArrowLeftHeadIcon.d.ts +0 -5
  92. package/dist/esm/assets/DoubleArrowLeftHeadIcon.d.ts.map +0 -1
  93. package/dist/esm/assets/DoubleArrowLeftHeadIcon.js +0 -3
  94. package/dist/esm/assets/DoubleArrowRightHeadIcon.d.ts +0 -5
  95. package/dist/esm/assets/DoubleArrowRightHeadIcon.d.ts.map +0 -1
  96. package/dist/esm/assets/DoubleArrowRightHeadIcon.js +0 -3
  97. package/dist/esm/assets/ExclamationFilledIcon.d.ts +0 -5
  98. package/dist/esm/assets/ExclamationFilledIcon.d.ts.map +0 -1
  99. package/dist/esm/assets/ExclamationFilledIcon.js +0 -3
  100. package/dist/esm/assets/FilterIcon.d.ts +0 -5
  101. package/dist/esm/assets/FilterIcon.d.ts.map +0 -1
  102. package/dist/esm/assets/FilterIcon.js +0 -3
  103. package/dist/esm/assets/LoadingSpinner.d.ts +0 -5
  104. package/dist/esm/assets/LoadingSpinner.d.ts.map +0 -1
  105. package/dist/esm/assets/LoadingSpinner.js +0 -3
  106. package/dist/esm/assets/RefreshIcon.d.ts +0 -5
  107. package/dist/esm/assets/RefreshIcon.d.ts.map +0 -1
  108. package/dist/esm/assets/RefreshIcon.js +0 -3
  109. package/dist/esm/assets/SearchIcon.d.ts +0 -5
  110. package/dist/esm/assets/SearchIcon.d.ts.map +0 -1
  111. package/dist/esm/assets/SearchIcon.js +0 -3
  112. package/dist/esm/assets/UpLeftArrowsIcon.d.ts +0 -5
  113. package/dist/esm/assets/UpLeftArrowsIcon.d.ts.map +0 -1
  114. package/dist/esm/assets/UpLeftArrowsIcon.js +0 -3
  115. package/dist/esm/assets/XCircleIcon.d.ts +0 -5
  116. package/dist/esm/assets/XCircleIcon.d.ts.map +0 -1
  117. package/dist/esm/assets/XCircleIcon.js +0 -3
  118. package/dist/esm/assets/XIcon.d.ts +0 -5
  119. package/dist/esm/assets/XIcon.d.ts.map +0 -1
  120. package/dist/esm/assets/XIcon.js +0 -3
  121. package/dist/esm/assets/index.d.ts +0 -22
  122. package/dist/esm/assets/index.d.ts.map +0 -1
  123. package/dist/esm/assets/index.js +0 -21
  124. package/dist/esm/components/Banner/index.d.ts +0 -3
  125. package/dist/esm/components/Banner/index.d.ts.map +0 -1
  126. package/dist/esm/components/Banner/index.js +0 -24
  127. package/dist/esm/components/BigModal/BigModal.d.ts +0 -15
  128. package/dist/esm/components/BigModal/BigModal.d.ts.map +0 -1
  129. package/dist/esm/components/BigModal/BigModal.js +0 -56
  130. package/dist/esm/components/Chart/BarChart.d.ts +0 -23
  131. package/dist/esm/components/Chart/BarChart.d.ts.map +0 -1
  132. package/dist/esm/components/Chart/BarChart.js +0 -110
  133. package/dist/esm/components/Chart/BarList.d.ts +0 -27
  134. package/dist/esm/components/Chart/BarList.d.ts.map +0 -1
  135. package/dist/esm/components/Chart/BarList.js +0 -148
  136. package/dist/esm/components/Chart/ChartError.d.ts +0 -10
  137. package/dist/esm/components/Chart/ChartError.d.ts.map +0 -1
  138. package/dist/esm/components/Chart/ChartError.js +0 -65
  139. package/dist/esm/components/Chart/ChartSkeleton.d.ts +0 -8
  140. package/dist/esm/components/Chart/ChartSkeleton.d.ts.map +0 -1
  141. package/dist/esm/components/Chart/ChartSkeleton.js +0 -19
  142. package/dist/esm/components/Chart/ChartTooltip.d.ts +0 -31
  143. package/dist/esm/components/Chart/ChartTooltip.d.ts.map +0 -1
  144. package/dist/esm/components/Chart/ChartTooltip.js +0 -234
  145. package/dist/esm/components/Chart/ChartTooltipFrame.d.ts +0 -6
  146. package/dist/esm/components/Chart/ChartTooltipFrame.d.ts.map +0 -1
  147. package/dist/esm/components/Chart/ChartTooltipFrame.js +0 -14
  148. package/dist/esm/components/Chart/ChartTooltipGroup.d.ts +0 -11
  149. package/dist/esm/components/Chart/ChartTooltipGroup.d.ts.map +0 -1
  150. package/dist/esm/components/Chart/ChartTooltipGroup.js +0 -23
  151. package/dist/esm/components/Chart/ChartTooltipRow.d.ts +0 -8
  152. package/dist/esm/components/Chart/ChartTooltipRow.d.ts.map +0 -1
  153. package/dist/esm/components/Chart/ChartTooltipRow.js +0 -41
  154. package/dist/esm/components/Chart/LineChart.d.ts +0 -29
  155. package/dist/esm/components/Chart/LineChart.d.ts.map +0 -1
  156. package/dist/esm/components/Chart/LineChart.js +0 -163
  157. package/dist/esm/components/Chart/PieChart.d.ts +0 -62
  158. package/dist/esm/components/Chart/PieChart.d.ts.map +0 -1
  159. package/dist/esm/components/Chart/PieChart.js +0 -195
  160. package/dist/esm/components/Dashboard/ChartComponent.d.ts +0 -4
  161. package/dist/esm/components/Dashboard/ChartComponent.d.ts.map +0 -1
  162. package/dist/esm/components/Dashboard/ChartComponent.js +0 -60
  163. package/dist/esm/components/Dashboard/DashboardFilter.d.ts +0 -38
  164. package/dist/esm/components/Dashboard/DashboardFilter.d.ts.map +0 -1
  165. package/dist/esm/components/Dashboard/DashboardFilter.js +0 -89
  166. package/dist/esm/components/Dashboard/DashboardSection.d.ts +0 -7
  167. package/dist/esm/components/Dashboard/DashboardSection.d.ts.map +0 -1
  168. package/dist/esm/components/Dashboard/DashboardSection.js +0 -22
  169. package/dist/esm/components/Dashboard/DashboardSectionContainer.d.ts +0 -3
  170. package/dist/esm/components/Dashboard/DashboardSectionContainer.d.ts.map +0 -1
  171. package/dist/esm/components/Dashboard/DashboardSectionContainer.js +0 -10
  172. package/dist/esm/components/Dashboard/DataLoader.d.ts +0 -44
  173. package/dist/esm/components/Dashboard/DataLoader.d.ts.map +0 -1
  174. package/dist/esm/components/Dashboard/DataLoader.js +0 -190
  175. package/dist/esm/components/Dashboard/MetricComponent.d.ts +0 -4
  176. package/dist/esm/components/Dashboard/MetricComponent.d.ts.map +0 -1
  177. package/dist/esm/components/Dashboard/MetricComponent.js +0 -133
  178. package/dist/esm/components/Dashboard/TableComponent.d.ts +0 -15
  179. package/dist/esm/components/Dashboard/TableComponent.d.ts.map +0 -1
  180. package/dist/esm/components/Dashboard/TableComponent.js +0 -62
  181. package/dist/esm/components/Dropdown/Dropdown.d.ts +0 -14
  182. package/dist/esm/components/Dropdown/Dropdown.d.ts.map +0 -1
  183. package/dist/esm/components/Dropdown/Dropdown.js +0 -69
  184. package/dist/esm/components/Dropdown/DropdownItem.d.ts +0 -11
  185. package/dist/esm/components/Dropdown/DropdownItem.d.ts.map +0 -1
  186. package/dist/esm/components/Dropdown/DropdownItem.js +0 -37
  187. package/dist/esm/components/Dropdown/index.d.ts +0 -3
  188. package/dist/esm/components/Dropdown/index.d.ts.map +0 -1
  189. package/dist/esm/components/Dropdown/index.js +0 -2
  190. package/dist/esm/components/Modal/Modal.d.ts +0 -15
  191. package/dist/esm/components/Modal/Modal.d.ts.map +0 -1
  192. package/dist/esm/components/Modal/Modal.js +0 -64
  193. package/dist/esm/components/Modal/index.d.ts +0 -2
  194. package/dist/esm/components/Modal/index.d.ts.map +0 -1
  195. package/dist/esm/components/Modal/index.js +0 -1
  196. package/dist/esm/components/QuillCard.d.ts +0 -9
  197. package/dist/esm/components/QuillCard.d.ts.map +0 -1
  198. package/dist/esm/components/QuillCard.js +0 -56
  199. package/dist/esm/components/QuillMultiSelect.d.ts +0 -11
  200. package/dist/esm/components/QuillMultiSelect.d.ts.map +0 -1
  201. package/dist/esm/components/QuillMultiSelect.js +0 -193
  202. package/dist/esm/components/QuillMultiSelectWithCombo.d.ts +0 -11
  203. package/dist/esm/components/QuillMultiSelectWithCombo.d.ts.map +0 -1
  204. package/dist/esm/components/QuillMultiSelectWithCombo.js +0 -215
  205. package/dist/esm/components/QuillSelect.d.ts +0 -6
  206. package/dist/esm/components/QuillSelect.d.ts.map +0 -1
  207. package/dist/esm/components/QuillSelect.js +0 -136
  208. package/dist/esm/components/QuillSelectWithCombo.d.ts +0 -6
  209. package/dist/esm/components/QuillSelectWithCombo.d.ts.map +0 -1
  210. package/dist/esm/components/QuillSelectWithCombo.js +0 -163
  211. package/dist/esm/components/QuillTable.d.ts +0 -31
  212. package/dist/esm/components/QuillTable.d.ts.map +0 -1
  213. package/dist/esm/components/QuillTable.js +0 -261
  214. package/dist/esm/components/ReportBuilder/AddColumnModal.d.ts +0 -34
  215. package/dist/esm/components/ReportBuilder/AddColumnModal.d.ts.map +0 -1
  216. package/dist/esm/components/ReportBuilder/AddColumnModal.js +0 -145
  217. package/dist/esm/components/ReportBuilder/AddLimitPopover.d.ts +0 -26
  218. package/dist/esm/components/ReportBuilder/AddLimitPopover.d.ts.map +0 -1
  219. package/dist/esm/components/ReportBuilder/AddLimitPopover.js +0 -36
  220. package/dist/esm/components/ReportBuilder/AddSortPopover.d.ts +0 -23
  221. package/dist/esm/components/ReportBuilder/AddSortPopover.d.ts.map +0 -1
  222. package/dist/esm/components/ReportBuilder/AddSortPopover.js +0 -73
  223. package/dist/esm/components/ReportBuilder/FilterModal.d.ts +0 -30
  224. package/dist/esm/components/ReportBuilder/FilterModal.d.ts.map +0 -1
  225. package/dist/esm/components/ReportBuilder/FilterModal.js +0 -576
  226. package/dist/esm/components/ReportBuilder/ast.d.ts +0 -523
  227. package/dist/esm/components/ReportBuilder/ast.d.ts.map +0 -1
  228. package/dist/esm/components/ReportBuilder/ast.js +0 -230
  229. package/dist/esm/components/ReportBuilder/bigDateMap.d.ts +0 -7
  230. package/dist/esm/components/ReportBuilder/bigDateMap.d.ts.map +0 -1
  231. package/dist/esm/components/ReportBuilder/bigDateMap.js +0 -687
  232. package/dist/esm/components/ReportBuilder/constants.d.ts +0 -117
  233. package/dist/esm/components/ReportBuilder/constants.d.ts.map +0 -1
  234. package/dist/esm/components/ReportBuilder/constants.js +0 -161
  235. package/dist/esm/components/ReportBuilder/convert.d.ts +0 -65
  236. package/dist/esm/components/ReportBuilder/convert.d.ts.map +0 -1
  237. package/dist/esm/components/ReportBuilder/convert.js +0 -717
  238. package/dist/esm/components/ReportBuilder/operators.d.ts +0 -462
  239. package/dist/esm/components/ReportBuilder/operators.d.ts.map +0 -1
  240. package/dist/esm/components/ReportBuilder/operators.js +0 -581
  241. package/dist/esm/components/ReportBuilder/pivot.d.ts +0 -16
  242. package/dist/esm/components/ReportBuilder/pivot.d.ts.map +0 -1
  243. package/dist/esm/components/ReportBuilder/pivot.js +0 -1
  244. package/dist/esm/components/ReportBuilder/postgres.d.ts +0 -150
  245. package/dist/esm/components/ReportBuilder/postgres.d.ts.map +0 -1
  246. package/dist/esm/components/ReportBuilder/postgres.js +0 -355
  247. package/dist/esm/components/ReportBuilder/schema.d.ts +0 -23
  248. package/dist/esm/components/ReportBuilder/schema.d.ts.map +0 -1
  249. package/dist/esm/components/ReportBuilder/schema.js +0 -1
  250. package/dist/esm/components/ReportBuilder/ui.d.ts +0 -119
  251. package/dist/esm/components/ReportBuilder/ui.d.ts.map +0 -1
  252. package/dist/esm/components/ReportBuilder/ui.js +0 -382
  253. package/dist/esm/components/ReportBuilder/util.d.ts +0 -76
  254. package/dist/esm/components/ReportBuilder/util.d.ts.map +0 -1
  255. package/dist/esm/components/ReportBuilder/util.js +0 -729
  256. package/dist/esm/components/UiComponents.d.ts +0 -221
  257. package/dist/esm/components/UiComponents.d.ts.map +0 -1
  258. package/dist/esm/components/UiComponents.js +0 -571
  259. package/dist/esm/components/selectUtils.d.ts +0 -9
  260. package/dist/esm/components/selectUtils.d.ts.map +0 -1
  261. package/dist/esm/components/selectUtils.js +0 -17
  262. package/dist/esm/contexts/BaseColorContext.d.ts +0 -4
  263. package/dist/esm/contexts/BaseColorContext.d.ts.map +0 -1
  264. package/dist/esm/contexts/BaseColorContext.js +0 -3
  265. package/dist/esm/contexts/HoveredValueContext.d.ts +0 -8
  266. package/dist/esm/contexts/HoveredValueContext.d.ts.map +0 -1
  267. package/dist/esm/contexts/HoveredValueContext.js +0 -5
  268. package/dist/esm/contexts/RootStylesContext.d.ts +0 -4
  269. package/dist/esm/contexts/RootStylesContext.d.ts.map +0 -1
  270. package/dist/esm/contexts/RootStylesContext.js +0 -3
  271. package/dist/esm/contexts/SelectedValueContext.d.ts +0 -8
  272. package/dist/esm/contexts/SelectedValueContext.d.ts.map +0 -1
  273. package/dist/esm/contexts/SelectedValueContext.js +0 -6
  274. package/dist/esm/contexts/index.d.ts +0 -5
  275. package/dist/esm/contexts/index.d.ts.map +0 -1
  276. package/dist/esm/contexts/index.js +0 -4
  277. package/dist/esm/hooks/index.d.ts +0 -6
  278. package/dist/esm/hooks/index.d.ts.map +0 -1
  279. package/dist/esm/hooks/index.js +0 -5
  280. package/dist/esm/hooks/useAstToFilterTree.d.ts +0 -11
  281. package/dist/esm/hooks/useAstToFilterTree.d.ts.map +0 -1
  282. package/dist/esm/hooks/useAstToFilterTree.js +0 -24
  283. package/dist/esm/hooks/useDashboard.d.ts +0 -7
  284. package/dist/esm/hooks/useDashboard.d.ts.map +0 -1
  285. package/dist/esm/hooks/useDashboard.js +0 -69
  286. package/dist/esm/hooks/useExport.d.ts +0 -6
  287. package/dist/esm/hooks/useExport.d.ts.map +0 -1
  288. package/dist/esm/hooks/useExport.js +0 -125
  289. package/dist/esm/hooks/useFormat.d.ts +0 -5
  290. package/dist/esm/hooks/useFormat.d.ts.map +0 -1
  291. package/dist/esm/hooks/useFormat.js +0 -25
  292. package/dist/esm/hooks/useInternalState.d.ts +0 -4
  293. package/dist/esm/hooks/useInternalState.d.ts.map +0 -1
  294. package/dist/esm/hooks/useInternalState.js +0 -14
  295. package/dist/esm/hooks/useOnClickOutside.d.ts +0 -3
  296. package/dist/esm/hooks/useOnClickOutside.d.ts.map +0 -1
  297. package/dist/esm/hooks/useOnClickOutside.js +0 -18
  298. package/dist/esm/hooks/useOnWindowResize.d.ts +0 -5
  299. package/dist/esm/hooks/useOnWindowResize.d.ts.map +0 -1
  300. package/dist/esm/hooks/useOnWindowResize.js +0 -14
  301. package/dist/esm/hooks/useQuill.d.ts +0 -37
  302. package/dist/esm/hooks/useQuill.d.ts.map +0 -1
  303. package/dist/esm/hooks/useQuill.js +0 -182
  304. package/dist/esm/hooks/useSelectOnKeyDown.d.ts +0 -3
  305. package/dist/esm/hooks/useSelectOnKeyDown.d.ts.map +0 -1
  306. package/dist/esm/hooks/useSelectOnKeyDown.js +0 -63
  307. package/dist/esm/hooks/useTheme.d.ts +0 -7
  308. package/dist/esm/hooks/useTheme.d.ts.map +0 -1
  309. package/dist/esm/hooks/useTheme.js +0 -10
  310. package/dist/esm/index.d.ts +0 -29
  311. package/dist/esm/index.d.ts.map +0 -1
  312. package/dist/esm/index.js +0 -16
  313. package/dist/esm/internals/ReportBuilder/PivotForm.d.ts +0 -28
  314. package/dist/esm/internals/ReportBuilder/PivotForm.d.ts.map +0 -1
  315. package/dist/esm/internals/ReportBuilder/PivotForm.js +0 -62
  316. package/dist/esm/internals/ReportBuilder/PivotList.d.ts +0 -39
  317. package/dist/esm/internals/ReportBuilder/PivotList.d.ts.map +0 -1
  318. package/dist/esm/internals/ReportBuilder/PivotList.js +0 -89
  319. package/dist/esm/internals/ReportBuilder/PivotModal.d.ts +0 -146
  320. package/dist/esm/internals/ReportBuilder/PivotModal.d.ts.map +0 -1
  321. package/dist/esm/internals/ReportBuilder/PivotModal.js +0 -1210
  322. package/dist/esm/lib/font.d.ts +0 -14
  323. package/dist/esm/lib/font.d.ts.map +0 -1
  324. package/dist/esm/lib/font.js +0 -13
  325. package/dist/esm/lib/index.d.ts +0 -4
  326. package/dist/esm/lib/index.d.ts.map +0 -1
  327. package/dist/esm/lib/index.js +0 -3
  328. package/dist/esm/lib/inputTypes.d.ts +0 -21
  329. package/dist/esm/lib/inputTypes.d.ts.map +0 -1
  330. package/dist/esm/lib/inputTypes.js +0 -55
  331. package/dist/esm/lib/utils.d.ts +0 -10
  332. package/dist/esm/lib/utils.d.ts.map +0 -1
  333. package/dist/esm/lib/utils.js +0 -35
  334. package/dist/esm/models/Columns.d.ts +0 -12
  335. package/dist/esm/models/Columns.d.ts.map +0 -1
  336. package/dist/esm/models/Columns.js +0 -1
  337. package/dist/esm/models/Filter.d.ts +0 -118
  338. package/dist/esm/models/Filter.d.ts.map +0 -1
  339. package/dist/esm/models/Filter.js +0 -98
  340. package/dist/esm/models/Pagination.d.ts +0 -10
  341. package/dist/esm/models/Pagination.d.ts.map +0 -1
  342. package/dist/esm/models/Pagination.js +0 -1
  343. package/dist/esm/models/Pivots.d.ts +0 -2
  344. package/dist/esm/models/Pivots.d.ts.map +0 -1
  345. package/dist/esm/models/Pivots.js +0 -1
  346. package/dist/esm/models/Report.d.ts +0 -103
  347. package/dist/esm/models/Report.d.ts.map +0 -1
  348. package/dist/esm/models/Report.js +0 -1
  349. package/dist/esm/models/Tables.d.ts +0 -8
  350. package/dist/esm/models/Tables.d.ts.map +0 -1
  351. package/dist/esm/models/Tables.js +0 -1
  352. package/dist/esm/utils/aggregate.d.ts +0 -4
  353. package/dist/esm/utils/aggregate.d.ts.map +0 -1
  354. package/dist/esm/utils/aggregate.js +0 -422
  355. package/dist/esm/utils/astFilterProcessing.d.ts +0 -36
  356. package/dist/esm/utils/astFilterProcessing.d.ts.map +0 -1
  357. package/dist/esm/utils/astFilterProcessing.js +0 -8084
  358. package/dist/esm/utils/astProcessing.d.ts +0 -26
  359. package/dist/esm/utils/astProcessing.d.ts.map +0 -1
  360. package/dist/esm/utils/astProcessing.js +0 -254
  361. package/dist/esm/utils/axisFormatter.d.ts +0 -20
  362. package/dist/esm/utils/axisFormatter.d.ts.map +0 -1
  363. package/dist/esm/utils/axisFormatter.js +0 -179
  364. package/dist/esm/utils/color.d.ts +0 -44
  365. package/dist/esm/utils/color.d.ts.map +0 -1
  366. package/dist/esm/utils/color.js +0 -425
  367. package/dist/esm/utils/columnProcessing.d.ts +0 -13
  368. package/dist/esm/utils/columnProcessing.d.ts.map +0 -1
  369. package/dist/esm/utils/columnProcessing.js +0 -260
  370. package/dist/esm/utils/constants.d.ts +0 -2
  371. package/dist/esm/utils/constants.d.ts.map +0 -1
  372. package/dist/esm/utils/constants.js +0 -1
  373. package/dist/esm/utils/crypto.d.ts +0 -2
  374. package/dist/esm/utils/crypto.d.ts.map +0 -1
  375. package/dist/esm/utils/crypto.js +0 -10
  376. package/dist/esm/utils/csv.d.ts +0 -6
  377. package/dist/esm/utils/csv.d.ts.map +0 -1
  378. package/dist/esm/utils/csv.js +0 -78
  379. package/dist/esm/utils/dashboard.d.ts +0 -13
  380. package/dist/esm/utils/dashboard.d.ts.map +0 -1
  381. package/dist/esm/utils/dashboard.js +0 -171
  382. package/dist/esm/utils/dataFetcher.d.ts +0 -3
  383. package/dist/esm/utils/dataFetcher.d.ts.map +0 -1
  384. package/dist/esm/utils/dataFetcher.js +0 -199
  385. package/dist/esm/utils/dataProcessing.d.ts +0 -11
  386. package/dist/esm/utils/dataProcessing.d.ts.map +0 -1
  387. package/dist/esm/utils/dataProcessing.js +0 -162
  388. package/dist/esm/utils/dates.d.ts +0 -20
  389. package/dist/esm/utils/dates.d.ts.map +0 -1
  390. package/dist/esm/utils/dates.js +0 -95
  391. package/dist/esm/utils/error.d.ts +0 -5
  392. package/dist/esm/utils/error.d.ts.map +0 -1
  393. package/dist/esm/utils/error.js +0 -8
  394. package/dist/esm/utils/errorProcessing.d.ts +0 -2
  395. package/dist/esm/utils/errorProcessing.d.ts.map +0 -1
  396. package/dist/esm/utils/errorProcessing.js +0 -5
  397. package/dist/esm/utils/filterConstants.d.ts +0 -34
  398. package/dist/esm/utils/filterConstants.d.ts.map +0 -1
  399. package/dist/esm/utils/filterConstants.js +0 -33
  400. package/dist/esm/utils/filterProcessing.d.ts +0 -10
  401. package/dist/esm/utils/filterProcessing.d.ts.map +0 -1
  402. package/dist/esm/utils/filterProcessing.js +0 -232
  403. package/dist/esm/utils/getDomain.d.ts +0 -8
  404. package/dist/esm/utils/getDomain.d.ts.map +0 -1
  405. package/dist/esm/utils/getDomain.js +0 -52
  406. package/dist/esm/utils/logging.d.ts +0 -2
  407. package/dist/esm/utils/logging.d.ts.map +0 -1
  408. package/dist/esm/utils/logging.js +0 -7
  409. package/dist/esm/utils/merge.d.ts +0 -2
  410. package/dist/esm/utils/merge.d.ts.map +0 -1
  411. package/dist/esm/utils/merge.js +0 -18
  412. package/dist/esm/utils/monacoConfig.d.ts +0 -21
  413. package/dist/esm/utils/monacoConfig.d.ts.map +0 -1
  414. package/dist/esm/utils/monacoConfig.js +0 -319
  415. package/dist/esm/utils/paginationProcessing.d.ts +0 -5
  416. package/dist/esm/utils/paginationProcessing.d.ts.map +0 -1
  417. package/dist/esm/utils/paginationProcessing.js +0 -25
  418. package/dist/esm/utils/parserBigQuery.d.ts +0 -6
  419. package/dist/esm/utils/parserBigQuery.d.ts.map +0 -1
  420. package/dist/esm/utils/parserBigQuery.js +0 -52
  421. package/dist/esm/utils/parserPostgres.d.ts +0 -3
  422. package/dist/esm/utils/parserPostgres.d.ts.map +0 -1
  423. package/dist/esm/utils/parserPostgres.js +0 -37
  424. package/dist/esm/utils/pivotConstructor.d.ts +0 -7
  425. package/dist/esm/utils/pivotConstructor.d.ts.map +0 -1
  426. package/dist/esm/utils/pivotConstructor.js +0 -151
  427. package/dist/esm/utils/pivotProcessing.d.ts +0 -17
  428. package/dist/esm/utils/pivotProcessing.d.ts.map +0 -1
  429. package/dist/esm/utils/pivotProcessing.js +0 -132
  430. package/dist/esm/utils/queryConstructor.d.ts +0 -7
  431. package/dist/esm/utils/queryConstructor.d.ts.map +0 -1
  432. package/dist/esm/utils/queryConstructor.js +0 -226
  433. package/dist/esm/utils/report.d.ts +0 -10
  434. package/dist/esm/utils/report.d.ts.map +0 -1
  435. package/dist/esm/utils/report.js +0 -183
  436. package/dist/esm/utils/schema.d.ts +0 -6
  437. package/dist/esm/utils/schema.d.ts.map +0 -1
  438. package/dist/esm/utils/schema.js +0 -153
  439. package/dist/esm/utils/styles.d.ts +0 -17
  440. package/dist/esm/utils/styles.d.ts.map +0 -1
  441. package/dist/esm/utils/styles.js +0 -16
  442. package/dist/esm/utils/tableProcessing.d.ts +0 -45
  443. package/dist/esm/utils/tableProcessing.d.ts.map +0 -1
  444. package/dist/esm/utils/tableProcessing.js +0 -300
  445. package/dist/esm/utils/textProcessing.d.ts +0 -6
  446. package/dist/esm/utils/textProcessing.d.ts.map +0 -1
  447. package/dist/esm/utils/textProcessing.js +0 -49
  448. package/dist/esm/utils/validation.d.ts +0 -9
  449. package/dist/esm/utils/validation.d.ts.map +0 -1
  450. package/dist/esm/utils/validation.js +0 -20
  451. package/dist/esm/utils/valueFormatter.d.ts +0 -29
  452. package/dist/esm/utils/valueFormatter.d.ts.map +0 -1
  453. package/dist/esm/utils/valueFormatter.js +0 -342
  454. package/dist/esm/utils/width.d.ts +0 -12
  455. package/dist/esm/utils/width.d.ts.map +0 -1
  456. package/dist/esm/utils/width.js +0 -21
@@ -1,3279 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useContext, useEffect, useRef, useState, } from 'react';
3
- import { DEFAULT_TAB_OPTIONS, MemoizedButton, MemoizedCheckbox, MemoizedDeleteButton, MemoizedHeader, MemoizedLabel, MemoizedSecondaryButton, MemoizedText, MemoizedPopover, QuillTabs, MemoizedModal, QuillChartBuilderInputRowContainer, QuillChartBuilderInputColumnContainer, MemoizedSubHeader, QuillErrorMessageComponent, QuillPivotRowContainer, QuillPivotColumnContainer, QuillColumnSearchEmptyState, QuillChartBuilderFormContainer, QuillLoadingComponent, QuillTableSQLEditorComponent, } from './components/UiComponents';
4
- import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, } from '@dnd-kit/core';
5
- import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable, } from '@dnd-kit/sortable';
6
- import { CSS as DND_CSS } from '@dnd-kit/utilities';
7
- import { ClientContext, CustomFieldContext, SchemaContext, ThemeContext, } from './Context';
8
- import { extractColumnish, getTableAliases, getTableNames, isBoolColumnType, isDateishColumnType, isNumericColumnType, isTextColumnType, } from './components/ReportBuilder/ast';
9
- import { ChartBuilderWithModal, createInitialFormData } from './ChartBuilder';
10
- import { QuillTextInput } from './components/UiComponents';
11
- import { QuillSidebar, CustomContainer, QuillSelectColumn, QuillDraggableColumn, QuillSidebarHeading, QuillFilterPopover, QuillSortPopover, QuillLimitPopover, FilterPopoverWrapper, } from './components/ReportBuilder/ui';
12
- import { generateCurrentPeriodPostgres, generateEqualsPostgres, generateLastNPeriodsPostgres, generatePreviousPeriodPostgres, } from './components/ReportBuilder/postgres';
13
- import { convertBigQuery, convertGroupBy, convertRemoveSimpleParentheses, convertStringComparison, convertWildcardColumns, convertUnaryToBinary, } from './components/ReportBuilder/convert';
14
- import { deepCopy, formatDateComparisonNode, getDateFilterInfo, isColumnComparison, isDateTruncEquals, isInTheLastInterval, isNodeEmptyCollection, isTheCurrentInterval, isThePreviousInterval, showNodeAsRow, removeNonSelectedTableReferences, isEquals, } from './components/ReportBuilder/util';
15
- import { getDefaultOperatorSubtrees, OPERATOR_GROUPS, } from './components/ReportBuilder/operators';
16
- import { hashCode } from './utils/crypto';
17
- import { defaultAST, defaultBoolComparison, defaultColumn, defaultEntry, defaultNumericComparison, defaultTable, defaultVariant, } from './components/ReportBuilder/constants';
18
- import AddColumnModal from './components/ReportBuilder/AddColumnModal';
19
- import { AddSortPopover, SortSentence, } from './components/ReportBuilder/AddSortPopover';
20
- import { PivotModal, generatePivotTable, } from './internals/ReportBuilder/PivotModal';
21
- import { snakeAndCamelCaseToTitleCase } from './utils/textProcessing';
22
- import { AddLimitPopover, LimitSentence, } from './components/ReportBuilder/AddLimitPopover';
23
- import { updateFirstChildWidth } from './utils/width';
24
- import { QuillSelectComponent } from './components/QuillSelect';
25
- import { QuillCard } from './components/QuillCard';
26
- import { getData } from './utils/dataFetcher';
27
- import { DATE_FORMAT_TYPES, quillFormat } from './utils/valueFormatter';
28
- import { getPossiblePivotFieldOptions, pivotToSql, } from './utils/pivotProcessing';
29
- import { getUniqueValuesByColumns, getDateRangeByColumns, getCountsByColumns, fetchTableByQuery, } from './utils/tableProcessing';
30
- import { useQuill } from './hooks/useQuill';
31
- import { getDataFromCloud } from './utils/dataFetcher';
32
- import { convertColumnInfoToColumnInternal, convertPostgresColumn, } from './utils/columnProcessing';
33
- import { getSelectFromAST, processApostrophe, processStarColumn, } from './utils/astProcessing';
34
- import PivotForm from './internals/ReportBuilder/PivotForm';
35
- import { getSchemaInfoWithCustomFields } from './utils/schema';
36
- import { getDateBucketFromRange } from './utils/dates';
37
- import FilterModal from './components/ReportBuilder/FilterModal';
38
- import { filterToAst, filterTreeToAst, } from './utils/astFilterProcessing';
39
- import useAstToFilterTree from './hooks/useAstToFilterTree';
40
- import { filterSentence } from './utils/filterProcessing';
41
- import { QuillMultiSelectComponentWithCombo } from './components/QuillMultiSelectWithCombo';
42
- import { shouldFetchMore, DEFAULT_PAGINATION, shouldSortInMemory, } from './utils/paginationProcessing';
43
- import { EMPTY_REPORT, formatRowsFromReport } from './utils/report';
44
- import { parseValueFromBigQueryDates } from './utils/dataProcessing';
45
- export const QUILL_SERVER = (typeof process !== 'undefined' && process?.env?.QUILL_SERVER_HOST) ||
46
- 'https://quill-344421.uc.r.appspot.com';
47
- /**
48
- * Quill Report Builder
49
- *
50
- * Allows non-technical users to build SQL queries using either UI or AI and
51
- * then edit them on the fly. Once users have constructed a query they like,
52
- * they can click a button and add that report to their dashboard or export it
53
- * as a CSV.
54
- *
55
- * @example
56
- * ```js
57
- * // Usage without custom components
58
- * <ReportBuilder />
59
- * ```
60
- *
61
- * @example
62
- * ```js
63
- * // You can also pass your own components
64
- * <ReportBuilder
65
- * initialTableName="transactions"
66
- * TableComponent={MyTable}
67
- * SelectComponent={MySelect}
68
- * ButtonComponent={MyButton}
69
- * PopoverComponent={MyPopover}
70
- * TextInputComponent={MyTextInput}
71
- * containerStyle={{ backgroundColor: 'white', padding: '10px' }}
72
- * />
73
- * ```
74
- *
75
- * ### Report Builder API
76
- * @see https://docs.quillsql.com/components/report-builder
77
- */
78
- export default function ReportBuilder({ initialTableName = '', onSubmitEditReport = () => void null, onSubmitCreateReport = () => void null, destinationDashboard = undefined, organizationName = '', ButtonComponent = MemoizedButton, SecondaryButtonComponent = MemoizedSecondaryButton, DeleteButtonComponent = MemoizedDeleteButton, ModalComponent = MemoizedModal, TextInputComponent = QuillTextInput, SelectComponent = QuillSelectComponent, MultiSelectComponent = QuillMultiSelectComponentWithCombo, TableComponent = QuillTableSQLEditorComponent, PopoverComponent = MemoizedPopover, TabsComponent = QuillTabs, CheckboxComponent = MemoizedCheckbox, SidebarComponent = QuillSidebar, ContainerComponent = CustomContainer, SelectColumnComponent = QuillSelectColumn, DraggableColumnComponent = QuillDraggableColumn, SidebarHeadingComponent = QuillSidebarHeading, FilterPopoverComponent = QuillFilterPopover, SortPopoverComponent = QuillSortPopover, LimitPopoverComponent = QuillLimitPopover, CardComponent = QuillCard, LabelComponent = MemoizedLabel, HeaderComponent = MemoizedHeader, SubHeaderComponent = MemoizedSubHeader, TextComponent = MemoizedText, ErrorMessageComponent = QuillErrorMessageComponent, ChartBuilderInputRowContainer = QuillChartBuilderInputRowContainer, ChartBuilderInputColumnContainer = QuillChartBuilderInputColumnContainer, PivotRowContainer = QuillPivotRowContainer, PivotColumnContainer = QuillPivotColumnContainer, LoadingComponent = QuillLoadingComponent, ColumnSearchEmptyState = QuillColumnSearchEmptyState, ChartBuilderFormContainer = QuillChartBuilderFormContainer, ChartBuilderModalComponent = MemoizedModal, isAdminEnabled = false, isAIEnabled = true, showChartBuilderTableFormatOptions = true, containerStyle, className, pivotRecommendationsEnabled = true, reportId, hideCopySQL = true, isChartBuilderHorizontalView = true, onClickChartElement, }) {
79
- const { data: report } = useQuill(reportId || '');
80
- const [aiPrompt, setAiPrompt] = useState('');
81
- const [errorMessage, setErrorMessage] = useState('');
82
- const [baseAst, setBaseAst] = useState(null);
83
- const [formData, setFormData] = useState(null);
84
- const [orderedColumnNames, setOrderedColumnNames] = useState([]);
85
- const [selectedColumns, setSelectedColumns] = useState([]);
86
- const [selectedOrderedColumns, setSelectedOrderedColumns] = useState([]);
87
- const [schema, setSchema] = useContext(SchemaContext);
88
- const [activeQuery, setActiveQuery] = useState('');
89
- const [activeEditItem, setActiveEditItem] = useState(null);
90
- const [activePath, setActivePath] = useState(null);
91
- const [openPopover, setOpenPopover] = useState(null);
92
- const [loading, setLoading] = useState(!!initialTableName);
93
- const [loadingSchema, setLoadingSchema] = useState(false);
94
- const [isChartBuilderOpen, setIsChartBuilderOpen] = useState(false);
95
- const [isPending, setIsPending] = useState(false);
96
- const [isCopying, setIsCopying] = useState(false);
97
- const [dataDisplayed, setDataDisplayed] = useState(false);
98
- const [rows, setRows] = useState([]);
99
- const [formattedRows, setFormattedRows] = useState([]);
100
- const [columns, setColumns] = useState([]);
101
- const [tempReport, setTempReport] = useState(EMPTY_REPORT);
102
- const [topLevelBinaryOperator, setTopLevelBinaryOperator] = useState('AND');
103
- const [uniqueValues, setUniqueValues] = useState({});
104
- const [pivot, setPivot] = useState(null);
105
- const [pivotData, setPivotData] = useState(null);
106
- const [createdPivots, setCreatedPivots] = useState([]);
107
- const [recommendedPivots, setRecommendedPivots] = useState([]);
108
- const [pivotPopUpTitle, setPivotPopUpTitle] = useState('Add pivot');
109
- const [showPivotPopover, setShowPivotPopover] = useState(false);
110
- const [isEditingPivot, setIsEditingPivot] = useState(false);
111
- const [initialChartLoad, setInitialChartLoad] = useState(false);
112
- const [askedAQuestion, setAskedAQuestion] = useState(false);
113
- const [selectedPivotIndex, setSelectedPivotIndex] = useState(-1);
114
- const [initialLoad, setInitialLoad] = useState(!!initialTableName || !!reportId);
115
- const [currentTable, setCurrentTable] = useState(initialTableName || '');
116
- const parentRef = useRef(null);
117
- const askAIContainerRef = useRef(null);
118
- const askAILoadingContainerRef = useRef(null);
119
- const [askAIInputWidth, setAskAIInputWidth] = useState(-1);
120
- const [askAILoadingContainerWidth, setAskAILoadingContainerWidth] = useState(-1);
121
- const [theme] = useContext(ThemeContext);
122
- const [pivotRowField, setPivotRowField] = useState(undefined);
123
- const [pivotColumnField, setPivotColumnField] = useState(undefined);
124
- const [pivotValueField, setPivotValueField] = useState(undefined);
125
- const [pivotAggregation, setPivotAggregation] = useState(undefined);
126
- const [dateRanges, setDateRanges] = useState(null);
127
- const [client] = useContext(ClientContext);
128
- // JANK: This is temp and stupid
129
- const [overrideRecommendations, setOverrideRecommendations] = useState(true);
130
- const [customFields, setCustomFields] = useContext(CustomFieldContext);
131
- const [fieldValuesMap, setFieldValuesMap] = useState({}); // Mapping of unique values per field, used in string filter 'in' and 'not in'
132
- const filterTree = useAstToFilterTree(formData, client); // Stores the state of filters
133
- useEffect(() => {
134
- if (!client) {
135
- return;
136
- }
137
- if (client.publicKey === '663416663aa9bc716e59a89d') {
138
- setOverrideRecommendations(false);
139
- }
140
- if (!loadingSchema) {
141
- fetchSchema();
142
- }
143
- }, [client]);
144
- useEffect(() => {
145
- updateFirstChildWidth(askAIContainerRef, setAskAIInputWidth, { gap: 12 });
146
- updateFirstChildWidth(askAILoadingContainerRef, setAskAILoadingContainerWidth, { gap: 12 });
147
- }, [dataDisplayed]);
148
- // Whenever unique values changes, update the fieldValuesMap, used in FilterModals
149
- useEffect(() => {
150
- const tables = getTableNames(baseAst);
151
- const table = tables.length === 1 ? tables[0] : initialTableName;
152
- const newFieldValues = {};
153
- if (uniqueValues[table]) {
154
- for (const field of Object.keys(uniqueValues[table])) {
155
- newFieldValues[field] = [];
156
- for (const value of Object.keys(uniqueValues[table][field])) {
157
- newFieldValues[field]?.push(value);
158
- }
159
- }
160
- }
161
- setFieldValuesMap(newFieldValues);
162
- }, [uniqueValues]);
163
- useEffect(() => {
164
- // Since the TextInput component takes a required numeric width parameter,
165
- // we dynamically calculate the width of this component here.
166
- function handleResize() {
167
- updateFirstChildWidth(askAIContainerRef, setAskAIInputWidth, { gap: 12 });
168
- updateFirstChildWidth(askAILoadingContainerRef, setAskAILoadingContainerWidth, { gap: 12 });
169
- }
170
- handleResize();
171
- window.addEventListener('resize', handleResize);
172
- return () => {
173
- window.removeEventListener('resize', handleResize);
174
- };
175
- }, []);
176
- const updatePivot = async (changeField, fieldKey) => {
177
- const newPivot = pivot;
178
- setTableLoading(true);
179
- // @ts-ignore
180
- newPivot[fieldKey] = changeField;
181
- if (fieldKey === 'rowField') {
182
- // check to see if the new rowField value is a date field
183
- const column = columns.find((c) => c.field === changeField);
184
- if (column?.jsType === 'date') {
185
- newPivot.rowFieldType = 'date';
186
- newPivot.sort = 'true';
187
- newPivot.sortField = changeField;
188
- newPivot.sortFieldType = column.format;
189
- newPivot.sortDirection = 'ASC';
190
- }
191
- else {
192
- newPivot.rowFieldType = 'string';
193
- newPivot.sort = undefined;
194
- }
195
- }
196
- let dateBucket = undefined;
197
- const tempDateRange = dateRanges && dateRanges[newPivot.rowField];
198
- if (tempDateRange) {
199
- dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
200
- }
201
- let distinctValuesForQuery = {};
202
- if (pivot.columnField) {
203
- distinctValuesForQuery = await getUniqueValuesByColumns([
204
- {
205
- field: pivot.columnField,
206
- label: pivot.columnField,
207
- format: 'string',
208
- },
209
- ], activeQuery, [], client, customFields);
210
- }
211
- const pivotedData = await generatePivotTable(pivot, rows, undefined, false, -1, undefined, dateBucket, tempReport, client, distinctValuesForQuery);
212
- setPivotData(pivotedData || []);
213
- const formattedRows = formatRows(pivotedData.rows, columns, true, newPivot.aggregationType);
214
- setPivot(newPivot);
215
- setFormattedRows(formattedRows);
216
- setTableLoading(false);
217
- };
218
- const enforceOrderOnColumns = (columnNames) => {
219
- if (pivot) {
220
- const rowName = pivot.rowField;
221
- const sortFn = (a, b) => a === rowName ? -1 : b === rowName ? 1 : 0;
222
- const columnsInPivot = getColumnsInPivotExpanded();
223
- return columnNames
224
- .sort(sortFn)
225
- .filter((c) => columnsInPivot.includes(c));
226
- }
227
- // make the columnNames match the order of the selectedOrderedColumns
228
- return columnNames.sort((a, b) => selectedOrderedColumns.indexOf(a) - selectedOrderedColumns.indexOf(b));
229
- // return columnNames;
230
- };
231
- const clearAllState = () => {
232
- // We're trying to not block the main thread while resetting all the state.
233
- // This shouldn't be an issue since the dispatches shouldn't block, but
234
- // this seems to work for now. ¯\_(ツ)_/¯
235
- setTimeout(() => {
236
- setAskedAQuestion(false);
237
- setAiPrompt('');
238
- setBaseAst(null);
239
- setFormData(null);
240
- setSelectedColumns([]);
241
- setActiveQuery('');
242
- setActiveEditItem(null);
243
- setActivePath(null);
244
- setOpenPopover(null);
245
- setLoading(false);
246
- setIsPending(false);
247
- setDataDisplayed(false);
248
- setRows([]);
249
- setColumns([]);
250
- setTopLevelBinaryOperator('AND');
251
- setErrorMessage('');
252
- setFormattedRows([]);
253
- // setUniqueValues({});
254
- setPivot(null);
255
- setPivotData(null);
256
- setRecommendedPivots([]);
257
- }, 0);
258
- };
259
- useEffect(() => {
260
- if (!client) {
261
- return;
262
- }
263
- if (!initialLoad && client.publicKey) {
264
- clearAllState();
265
- }
266
- }, [client]);
267
- useEffect(() => {
268
- if (activePath !== null) {
269
- // update the modal with the new subtree
270
- setActiveEditItem(getByKey(formData, activePath));
271
- }
272
- }, [formData]);
273
- const formatRows = (rows, columns, pivot, aggregationType) => {
274
- const copiedRows = deepCopy(rows);
275
- if (pivot) {
276
- const formattedRows = copiedRows.map((row) => {
277
- const formattedRow = row;
278
- Object.keys(row).forEach((key) => {
279
- const column = columns.find((c) => c.field === key);
280
- let format = 'string';
281
- if (!column) {
282
- format =
283
- aggregationType === 'count'
284
- ? 'whole_number'
285
- : 'two_decimal_places';
286
- }
287
- else {
288
- format = DATE_FORMAT_TYPES.includes(column.format)
289
- ? 'MMM_yyyy'
290
- : 'string';
291
- }
292
- const formattedValue = quillFormat({
293
- value: row[key],
294
- format,
295
- });
296
- formattedRow[key] = formattedValue;
297
- });
298
- return formattedRow;
299
- });
300
- return formattedRows;
301
- }
302
- else {
303
- const formattedRows = copiedRows.map((row) => {
304
- return columns.reduce((formattedRow, column) => {
305
- // Apply the format function to each field in the row
306
- const formattedValue = quillFormat({
307
- value: row[column.field],
308
- format: column.format,
309
- });
310
- formattedRow[column.field] = formattedValue;
311
- return formattedRow;
312
- }, {});
313
- });
314
- return formattedRows;
315
- }
316
- };
317
- const getByKey = (formData, path) => {
318
- if (!path)
319
- return deepCopy(formData);
320
- // Function to immutably update or delete nodes based on their path
321
- const paths = path.split('.');
322
- let current = deepCopy(formData);
323
- for (let i = 0; i < paths.length; i++) {
324
- if (current[paths[i]]) {
325
- current = current[paths[i]];
326
- }
327
- }
328
- return current;
329
- };
330
- const copySQLToClipboard = () => {
331
- let query = activeQuery;
332
- if (pivot) {
333
- query = pivotToSql(pivot, activeQuery, columns);
334
- }
335
- setIsCopying(true);
336
- navigator.clipboard.writeText(query);
337
- setTimeout(() => setIsCopying(false), 800);
338
- };
339
- const clearCheckboxes = () => {
340
- const checkboxes = uniqueValues;
341
- const newValues = {};
342
- for (const table of Object.keys(checkboxes)) {
343
- newValues[table] = {};
344
- for (const column of Object.keys(checkboxes[table])) {
345
- newValues[table][column] = {};
346
- for (const variant of Object.keys(checkboxes[table][column])) {
347
- newValues[table][column][variant] = false;
348
- }
349
- }
350
- }
351
- setUniqueValues(newValues);
352
- };
353
- const fetchSqlQuery = async (ast, formData, fetchData = true) => {
354
- if (fetchData) {
355
- setLoading(true);
356
- }
357
- setErrorMessage('');
358
- try {
359
- const where = formData ? formData : ast?.where || null;
360
- const response = await fetch(`${QUILL_SERVER}/sqlify`, {
361
- method: 'POST',
362
- headers: {
363
- 'Content-Type': 'application/json',
364
- },
365
- body: JSON.stringify({
366
- ast: { ...ast, where },
367
- publicKey: client.publicKey,
368
- useNewNodeSql: true, // new flag
369
- }),
370
- });
371
- const data = await response.json();
372
- setActiveQuery(data.query);
373
- if (fetchData) {
374
- fetchUponChange(ast, formData);
375
- }
376
- return data.query;
377
- }
378
- catch (error) {
379
- setLoading(false);
380
- console.error(error);
381
- }
382
- };
383
- const getUniqueStringValues = async (columns, tableName) => {
384
- const convertedStringColumns = columns
385
- .filter((column) => {
386
- return isTextColumnType(column.fieldType);
387
- })
388
- .map((column) => convertColumnInfoToColumnInternal(column));
389
- const stringNames = convertedStringColumns.map((column) => column.field);
390
- const smallStringColumns = await getCountsByColumns(convertedStringColumns, `Select ${stringNames.join(', ')} from ${tableName}`, client, customFields);
391
- const smallStringNames = smallStringColumns.map((column) => column.field);
392
- const newUniqueValues = await getUniqueValuesByColumns(smallStringColumns, `Select ${smallStringNames.join(', ')} from ${tableName}`, [], client, customFields);
393
- const joinedUniqueValues = deepCopy(uniqueValues);
394
- joinedUniqueValues[tableName] = newUniqueValues;
395
- return joinedUniqueValues;
396
- };
397
- const getDateRanges = async (columns, tableName) => {
398
- const dateColumns = columns.filter((column) => {
399
- return column.fieldType === 'date';
400
- });
401
- if (dateColumns.length === 0) {
402
- return {};
403
- }
404
- const dateColumnNames = dateColumns.map((column) => {
405
- //@ts-ignore
406
- return column.field || column.name;
407
- });
408
- const dateRanges = await getDateRangeByColumns(dateColumns, `Select ${dateColumnNames.join(', ')} from ${tableName}`, client, customFields);
409
- return dateRanges;
410
- };
411
- // It's just like getColumnsInPivot but we expand the columnField
412
- // if there is one to include all the variants just like it would
413
- // show up in the table. (eg. category -> ...[Fuel, Food, Other])
414
- const getColumnsInPivotExpanded = () => {
415
- if (!pivot)
416
- return [];
417
- const tables = getTableNames(baseAst);
418
- if (tables.length !== 1)
419
- return [];
420
- const result = [];
421
- const table = tables[0];
422
- const { valueField, rowField, columnField } = pivot;
423
- if (columnField &&
424
- uniqueValues[table] &&
425
- uniqueValues[table][columnField]) {
426
- result.push(...Object.keys(uniqueValues[table][columnField]));
427
- }
428
- result.push(valueField, rowField);
429
- return result.filter(Boolean);
430
- };
431
- const loadTable = async (tables) => {
432
- if (!tables)
433
- return;
434
- setLoading(true);
435
- const tableInfo = tables.find((tableInfo) => tableInfo.name === initialTableName);
436
- if (tableInfo) {
437
- const newUniqueValues = await getUniqueStringValues(tableInfo.columns, initialTableName);
438
- if (hashCode(uniqueValues) !== hashCode(newUniqueValues)) {
439
- setUniqueValues(newUniqueValues);
440
- }
441
- const dateRangesTemp = await getDateRanges(tableInfo.columns, initialTableName);
442
- setDateRanges(dateRangesTemp);
443
- }
444
- const columnsForTable = tables
445
- .find((t) => t.name === initialTableName)
446
- ?.columns.map((c) => c.name)
447
- .sort((a, b) => {
448
- const aIsId = a.endsWith('.id') ||
449
- a.endsWith('_id') ||
450
- a.endsWith('Id') ||
451
- a === 'id';
452
- const bIsId = b.endsWith('.id') ||
453
- b.endsWith('_id') ||
454
- b.endsWith('Id') ||
455
- b === 'id';
456
- if (aIsId && !bIsId)
457
- return 1;
458
- if (bIsId && !aIsId)
459
- return -1;
460
- return 0;
461
- });
462
- await handleAsk(`get ${columnsForTable} from ${initialTableName}`);
463
- setInitialLoad(false);
464
- };
465
- const fetchSchema = async () => {
466
- try {
467
- setLoadingSchema(true);
468
- const { schemaData, customFieldsByTable } = await getSchemaInfoWithCustomFields(client, 'rb');
469
- setCustomFields(customFieldsByTable);
470
- setSchema(schemaData ?? []);
471
- setOrderedColumnNames((schemaData ?? []).flatMap((table) => table.columns
472
- .map((c) => `${table.name}.${c.name}`)
473
- .sort((a, b) => {
474
- const aIsId = a.endsWith('.id') ||
475
- a.endsWith('_id') ||
476
- a.endsWith('Id') ||
477
- a === 'id';
478
- const bIsId = b.endsWith('.id') ||
479
- b.endsWith('_id') ||
480
- b.endsWith('Id') ||
481
- b === 'id';
482
- if (aIsId && !bIsId)
483
- return 1;
484
- if (bIsId && !aIsId)
485
- return -1;
486
- return 0;
487
- })));
488
- if (initialTableName) {
489
- await loadTable(schemaData);
490
- }
491
- setLoadingSchema(false);
492
- setInitialLoad(false);
493
- return schemaData;
494
- }
495
- catch (error) {
496
- console.error(error);
497
- }
498
- };
499
- useEffect(() => {
500
- const loadChart = async () => {
501
- setInitialChartLoad(true);
502
- // @ts-ignore THIS PROCESS SHOULD BE UPDATED TO NOT USE USEQUILL
503
- if (!report || report.referencedTables.length !== 1) {
504
- setInitialChartLoad(false);
505
- return;
506
- }
507
- try {
508
- // @ts-ignore THIS PROCESS SHOULD BE UPDATED TO NOT USE USEQUILL
509
- const tableName = report.referencedTables[0];
510
- if (!tableName) {
511
- return;
512
- }
513
- const resp = await getDataFromCloud(client, `astify`, {
514
- // @ts-ignore THIS PROCESS SHOULD BE UPDATED TO NOT USE USEQUILL
515
- query: report.queryString,
516
- useNewNodeSql: true,
517
- });
518
- if (resp.success === false) {
519
- setErrorMessage(resp.message);
520
- return;
521
- }
522
- const ast = getSelectFromAST(resp.ast);
523
- let convertedAst = processStarColumn(ast, report.columns);
524
- processApostrophe(convertedAst, ['type', 'value']);
525
- convertedAst = convertBigQuery(convertedAst);
526
- const schemaInfo = schema.length !== 0 ? schema : await fetchSchema();
527
- let newAst;
528
- let groupByPivot = {};
529
- ({ ast: newAst, pivot: groupByPivot } = convertGroupBy(convertedAst,
530
- // @ts-ignore
531
- report.pivot, schemaInfo));
532
- if (convertedAst.where) {
533
- setFormData(deepCopy(convertedAst.where));
534
- }
535
- // @ts-ignore THIS PROCESS SHOULD BE UPDATED TO NOT USE USEQUILL
536
- setActiveQuery(report.queryString);
537
- newAst = groupByPivot ? newAst : convertedAst;
538
- const initialRows = await fetchUponChange(newAst, undefined);
539
- if (initialRows.error) {
540
- setBaseAst(null);
541
- setErrorMessage(initialRows.message);
542
- setInitialChartLoad(false);
543
- return;
544
- }
545
- setBaseAst(newAst);
546
- const tableInfo = schemaInfo.find((table) => table.name === tableName);
547
- let newUniqueValues = undefined;
548
- let dateRangesTemp = undefined;
549
- if (tableName) {
550
- newUniqueValues = await getUniqueStringValues(tableInfo.columns, tableName);
551
- setUniqueValues(newUniqueValues);
552
- dateRangesTemp = await getDateRanges(tableInfo.columns, tableName);
553
- setDateRanges(dateRangesTemp);
554
- }
555
- if (groupByPivot) {
556
- // @ts-ignore
557
- setPivotRowField(groupByPivot.rowField);
558
- // @ts-ignore
559
- setPivotAggregation(groupByPivot.aggregationType);
560
- // @ts-ignore
561
- setPivotColumnField(groupByPivot.columnField);
562
- // @ts-ignore
563
- setPivotValueField(groupByPivot.valueField);
564
- setPivot(groupByPivot);
565
- let dateBucket = undefined;
566
- const tempDateRange = dateRangesTemp &&
567
- groupByPivot.rowField &&
568
- dateRangesTemp[groupByPivot.rowField];
569
- if (tempDateRange) {
570
- dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
571
- }
572
- const pivotedData = await generatePivotTable(
573
- // @ts-ignore
574
- groupByPivot, initialRows, tempDateRange, false, -1, undefined, dateBucket, report, client, newUniqueValues[tableName]);
575
- setPivotData(pivotedData || []);
576
- const formattedRows = formatRows(pivotedData.rows, report.columns, true,
577
- // @ts-ignore
578
- groupByPivot.aggregationType);
579
- setFormattedRows(formattedRows);
580
- }
581
- else {
582
- const formattedRows = formatRows(report.rows, report.columns);
583
- setFormattedRows(formattedRows);
584
- }
585
- setCurrentTable(tableName);
586
- }
587
- catch (error) {
588
- console.error(error);
589
- setErrorMessage('Error loading report');
590
- }
591
- // This handles a flashing issue
592
- setTimeout(() => {
593
- setInitialChartLoad(false);
594
- }, 500);
595
- };
596
- // @ts-ignore THIS PROCESS SHOULD BE UPDATED TO NOT USE USEQUILL
597
- if (report && report.referencedTables.length === 1) {
598
- loadChart();
599
- }
600
- }, [report]);
601
- useEffect(() => {
602
- if (schema.length === 0) {
603
- fetchSchema();
604
- }
605
- }, [schema, initialTableName, reportId]);
606
- const updateFormData = (updates, { isDeletion = false, isInsertion = false, isReplaceSubtree = false, isAddVariant = false, isDeleteVariant = false, topLevelBinOp = 'OR', isCondition = undefined, }) => {
607
- // Function to immutably update or delete nodes based on their path
608
- // TODO: fix the following horible code
609
- updates.forEach(({ path, value }) => {
610
- const globalPath = [
611
- activePath ?? isDeletion ? path : '',
612
- isDeletion || isReplaceSubtree ? '' : path,
613
- ]
614
- .filter(Boolean)
615
- .join('.');
616
- const paths = globalPath.split('.').filter((p) => p);
617
- if (paths.length === 0 && !isInsertion && !isReplaceSubtree) {
618
- setFormData(null);
619
- const newAst = deepCopy({
620
- ...defaultAST,
621
- ...baseAst,
622
- ...(!baseAst?.columns && {
623
- columns: getAllPossibleColumns().map((c) => {
624
- const newColumn = deepCopy(defaultColumn);
625
- newColumn.expr.column = c.name;
626
- return newColumn;
627
- }),
628
- }),
629
- ...(!baseAst?.from && {
630
- from: [{ ...defaultTable, table: initialTableName }],
631
- }),
632
- where: null,
633
- });
634
- setBaseAst(newAst);
635
- fetchSqlQuery(newAst, null);
636
- return;
637
- }
638
- if (!formData && isInsertion) {
639
- const newAst = deepCopy({
640
- ...defaultAST,
641
- ...baseAst,
642
- ...(!baseAst?.columns && {
643
- columns: getAllPossibleColumns().map((c) => {
644
- const newColumn = deepCopy(defaultColumn);
645
- newColumn.expr.column = c.name;
646
- return newColumn;
647
- }),
648
- }),
649
- ...(!baseAst?.from && {
650
- from: [{ ...defaultTable, table: initialTableName }],
651
- }),
652
- where: value,
653
- });
654
- setFormData(value);
655
- setBaseAst(newAst);
656
- fetchSqlQuery(newAst, value);
657
- return;
658
- }
659
- let newState = deepCopy(formData);
660
- let current = newState;
661
- let parent = null;
662
- let parentKey = null;
663
- for (let i = 0; i < paths.length - 1; i++) {
664
- if (current[paths[i]]) {
665
- parent = current;
666
- parentKey = paths[i];
667
- current = current[paths[i]];
668
- }
669
- }
670
- const lastKey = paths[paths.length - 1];
671
- if (isDeletion) {
672
- if (lastKey === 'left' || lastKey === 'right') {
673
- if (parent) {
674
- if (lastKey === 'right') {
675
- parent[parentKey] = parent[parentKey].left;
676
- }
677
- else {
678
- parent[parentKey] = parent[parentKey].right;
679
- }
680
- }
681
- else {
682
- delete current[lastKey];
683
- if (newState?.left && !newState?.right) {
684
- newState = newState.left;
685
- }
686
- else if (newState?.right && !newState?.left) {
687
- newState = newState.right;
688
- }
689
- }
690
- }
691
- }
692
- else if (isInsertion) {
693
- newState = {
694
- type: 'binary_expr',
695
- operator: topLevelBinOp,
696
- isCondition: isCondition,
697
- left: newState,
698
- right: value,
699
- };
700
- }
701
- else if (isAddVariant) {
702
- const newVariant = deepCopy(defaultVariant);
703
- if (value) {
704
- newVariant.args.value[0].value = value;
705
- // if there is already a single default value there,
706
- // let's remove it so when we push we replace.
707
- if (current[lastKey].length === 1 &&
708
- current[lastKey][0].args.value[0].value === '') {
709
- current[lastKey].pop();
710
- }
711
- }
712
- current[lastKey].push(newVariant);
713
- }
714
- else if (isDeleteVariant) {
715
- if (value) {
716
- const argList = current[lastKey];
717
- argList.splice(argList.findIndex((arg) => arg.args.value[0].value === name), 1);
718
- }
719
- else {
720
- current[lastKey].pop();
721
- }
722
- }
723
- else if (isReplaceSubtree) {
724
- if (lastKey) {
725
- current[lastKey] = value;
726
- }
727
- else {
728
- newState = value;
729
- }
730
- }
731
- else {
732
- if (typeof current[lastKey] === 'object' && current[lastKey] !== null) {
733
- current[lastKey].value = value;
734
- }
735
- else {
736
- current[lastKey] = value;
737
- }
738
- }
739
- setFormData(newState);
740
- const newAst = {
741
- ...defaultAST,
742
- ...baseAst,
743
- ...(!baseAst?.columns && {
744
- columns: getAllPossibleColumns().map((c) => {
745
- const newColumn = deepCopy(defaultColumn);
746
- newColumn.expr.column = c.name;
747
- return newColumn;
748
- }),
749
- }),
750
- ...(!baseAst?.from && {
751
- from: [{ ...defaultTable, table: initialTableName }],
752
- }),
753
- where: { ...newState },
754
- };
755
- setBaseAst(newAst);
756
- fetchSqlQuery(newAst, newState);
757
- });
758
- };
759
- // TODO: Merge this function with the updateFormData function
760
- const updateActiveItem = (updates, { isDeletion = false, isInsertion = false, isReplaceSubtree = false, isAddVariant = false, isDeleteVariant = false, column = undefined, }) => {
761
- let newState = deepCopy(activeEditItem);
762
- updates.forEach(({ path, value }) => {
763
- let current = newState;
764
- const globalPath = path;
765
- const paths = globalPath.split('.').filter((p) => p);
766
- if (paths.length === 0 && !isInsertion && !isReplaceSubtree) {
767
- setActiveEditItem(null);
768
- return;
769
- }
770
- const isOperatorChange = paths[paths.length - 1] === 'operator';
771
- let parent = null;
772
- let parentKey = null;
773
- for (let i = 0; i < paths.length - 1; i++) {
774
- let currentPath = paths[i];
775
- let index;
776
- if (paths[i].includes('||')) {
777
- const splitPath = paths[i].split('||');
778
- currentPath = splitPath[0];
779
- index = splitPath[1];
780
- }
781
- if (current[currentPath]) {
782
- parent = current;
783
- parentKey = currentPath;
784
- current = current[currentPath];
785
- }
786
- if (index) {
787
- current = current[parseInt(index)];
788
- }
789
- }
790
- const lastKey = paths[paths.length - 1];
791
- if (isDeletion) {
792
- if (lastKey === 'left' || lastKey === 'right') {
793
- if (parent) {
794
- if (lastKey === 'right') {
795
- parent[parentKey] = parent[parentKey].left;
796
- }
797
- else {
798
- parent[parentKey] = parent[parentKey].right;
799
- }
800
- }
801
- else {
802
- delete current[lastKey];
803
- if (newState?.left && !newState?.right) {
804
- newState = newState.left;
805
- }
806
- else if (newState?.right && !newState?.left) {
807
- newState = newState.right;
808
- }
809
- }
810
- }
811
- }
812
- else if (isInsertion) {
813
- const columns = getAllPossibleColumns();
814
- const defaultColumn = columns[0].name;
815
- // TODO: I think this is a bug, take a closer look here
816
- newState = {
817
- type: 'binary_expr',
818
- operator: 'AND',
819
- left: newState,
820
- right: {
821
- ...defaultEntry,
822
- left: {
823
- ...defaultEntry.left,
824
- column: defaultColumn,
825
- },
826
- },
827
- };
828
- }
829
- else if (isAddVariant) {
830
- const newVariant = deepCopy(defaultVariant);
831
- if (value) {
832
- newVariant.args.value[0].value = value;
833
- // if there is already a single default value there,
834
- // let's remove it so when we push we replace.
835
- if (current[lastKey].length === 1 &&
836
- current[lastKey][0].args.value[0].value === '') {
837
- current[lastKey].pop();
838
- }
839
- }
840
- current[lastKey].push(newVariant);
841
- }
842
- else if (isDeleteVariant) {
843
- if (value) {
844
- const argList = current[lastKey];
845
- argList.splice(argList.findIndex((arg) => arg.args.value[0].value === value), 1);
846
- }
847
- else {
848
- current[lastKey].pop();
849
- }
850
- // add back in a phantom element to prevent app from crashing
851
- // when the user removes all variants and hits save.
852
- if (current[lastKey].length === 0) {
853
- const newVariant = deepCopy(defaultVariant);
854
- newVariant.args.value[0].value = '';
855
- current[lastKey].push(newVariant);
856
- }
857
- }
858
- else if (isReplaceSubtree) {
859
- if (lastKey) {
860
- current[lastKey] = value;
861
- }
862
- else {
863
- newState = value;
864
- }
865
- }
866
- else if (isOperatorChange) {
867
- const newOp = value;
868
- const oldOp = current[lastKey];
869
- if (OPERATOR_GROUPS[oldOp] === OPERATOR_GROUPS[newOp]) {
870
- current[lastKey] = value;
871
- }
872
- else {
873
- const group = OPERATOR_GROUPS[newOp];
874
- const subtree = getDefaultOperatorSubtrees(group, newOp, column, '', client.databaseType);
875
- if (parentKey) {
876
- parent[parentKey] = deepCopy(subtree);
877
- }
878
- else {
879
- newState = deepCopy(subtree);
880
- }
881
- }
882
- }
883
- else {
884
- if (typeof current[lastKey] === 'object' && current[lastKey] !== null) {
885
- current[lastKey].value = value;
886
- }
887
- else {
888
- current[lastKey] = value;
889
- }
890
- }
891
- });
892
- // Function to immutably update or delete nodes based on their path
893
- setActiveEditItem(newState);
894
- };
895
- const handleChange = (updates) => {
896
- const callback = isPending ? updateActiveItem : updateFormData;
897
- callback(updates, {});
898
- };
899
- const handleChangeText = (updates) => {
900
- const callback = isPending ? updateActiveItem : updateFormData;
901
- callback(updates, {});
902
- };
903
- // Function to handle operator changes
904
- const handleOperatorChange = (value, node, keyPrefix, column = null) => {
905
- if (!keyPrefix) {
906
- setTopLevelBinaryOperator(value);
907
- }
908
- if (isPending) {
909
- updateActiveItem([{ path: keyPrefix + 'operator', value }], { column });
910
- }
911
- else {
912
- updateFormData([{ path: keyPrefix + 'operator', value }], { column });
913
- }
914
- };
915
- // Function to replace an entire subtree with a given value.
916
- const handleReplaceSubtree = (keyPrefix, newValue, pending = isPending) => {
917
- const callback = pending ? updateActiveItem : updateFormData;
918
- callback([{ path: keyPrefix, value: newValue }], {
919
- isReplaceSubtree: true,
920
- });
921
- };
922
- // Function to handle the insertion of expressions
923
- const handleInsertion = (value, op = 'OR', isCondition = undefined) => {
924
- updateFormData([{ path: '', value }], {
925
- isInsertion: true,
926
- topLevelBinOp: op,
927
- isCondition,
928
- });
929
- };
930
- // Function to handle the insertion of expr_list variants
931
- const handleInsertVariant = (key, name = null) => {
932
- // note: if name, treat that as the name of the value to insert
933
- const callback = isPending ? updateActiveItem : updateFormData;
934
- callback([{ path: key, value: name }], { isAddVariant: true });
935
- };
936
- // Function to handle the insertion of expr_list variants
937
- const handleDeleteVariant = (key, name = null) => {
938
- // note: if name, treat that as the name of the valeu to delete
939
- const callback = isPending ? updateActiveItem : updateFormData;
940
- callback([{ path: key, value: name }], { isDeleteVariant: true });
941
- };
942
- const getColumnValueForColumnComparison = (node) => node.left.value ??
943
- node.left.column ??
944
- node.left.args?.value[0]?.value ??
945
- node.left.args?.value[0]?.column ??
946
- undefined;
947
- /**
948
- * Searches for the column by name and returns the field type.
949
- *
950
- * Searches the known schema and returns the fieldType of the first column
951
- * it can find with the given name. Will first search through the current
952
- * list of fields in the current query if any, then will default to searching
953
- * through the whole schema.
954
- *
955
- * If more than one column exist with the given name, it will return the first
956
- * one that it finds. This might not be the one that you intended.
957
- *
958
- * TODO: pass an optional table param to limit the search to a given table.
959
- *
960
- * @param columnName the name to search for.
961
- * @returns the fieldType string or undefined if not found.
962
- */
963
- const getColumnTypeByName = (columnName) => {
964
- const column = columns.find((col) => col.field === columnName);
965
- return column?.fieldType;
966
- };
967
- const emptyPivotColumns = () => {
968
- if (pivot && pivot.rowField && pivot.columnField && pivot.valueField) {
969
- return [
970
- { label: snakeAndCamelCaseToTitleCase(pivot.rowField) },
971
- { label: snakeAndCamelCaseToTitleCase(pivot.columnField) },
972
- ];
973
- }
974
- else if (pivot && pivot.rowField && pivot.valueField) {
975
- return [
976
- { label: snakeAndCamelCaseToTitleCase(pivot.rowField) },
977
- { label: snakeAndCamelCaseToTitleCase(pivot.valueField) },
978
- ];
979
- }
980
- else {
981
- return [{ label: snakeAndCamelCaseToTitleCase(pivot.valueField) }];
982
- }
983
- };
984
- const [previousPage, setPreviousPage] = useState(0);
985
- const [currentProcessing, setCurrentProcessing] = useState({
986
- page: DEFAULT_PAGINATION,
987
- });
988
- const [numberOfRows, setNumberOfRows] = useState(0);
989
- const [tableLoading, setTableLoading] = useState(false);
990
- const onPageChange = (page) => {
991
- if (currentProcessing.page &&
992
- shouldFetchMore(DEFAULT_PAGINATION, page, previousPage)) {
993
- const newPagination = { ...currentProcessing.page, page };
994
- const updatedProcessing = { ...currentProcessing, page: newPagination };
995
- setCurrentProcessing(updatedProcessing);
996
- handleRunQuery(updatedProcessing);
997
- }
998
- if (page > previousPage) {
999
- setPreviousPage(page);
1000
- }
1001
- };
1002
- const onSortChange = (sort) => {
1003
- if (shouldSortInMemory(DEFAULT_PAGINATION, numberOfRows, !!pivot)) {
1004
- return;
1005
- }
1006
- const updatedProcessing = { page: DEFAULT_PAGINATION, sort };
1007
- handleRunQuery(updatedProcessing, true);
1008
- setCurrentProcessing(updatedProcessing);
1009
- setPreviousPage(0);
1010
- };
1011
- const handleRunQuery = async (processing, resetRows = false) => {
1012
- try {
1013
- setErrorMessage('');
1014
- setTableLoading(true);
1015
- const tableInfo = await fetchTableByQuery(activeQuery, client, processing, customFields);
1016
- if (tableInfo.error) {
1017
- throw new Error(tableInfo.error);
1018
- }
1019
- else if (tableInfo.rows.length === 0) {
1020
- throw new Error('No data found');
1021
- }
1022
- if (tableInfo.rowCount) {
1023
- setNumberOfRows(tableInfo.rowCount);
1024
- }
1025
- setCurrentProcessing(processing);
1026
- let tempRows = [...rows, ...tableInfo.rows];
1027
- if (resetRows) {
1028
- tempRows = tableInfo.rows;
1029
- }
1030
- setRows(tempRows);
1031
- setFormattedRows(formatRowsFromReport({ rows: tempRows, columns: tableInfo.columns }));
1032
- setColumns(tableInfo.columns);
1033
- setTableLoading(false);
1034
- }
1035
- catch (e) {
1036
- setTableLoading(false);
1037
- setErrorMessage('Failed to run SQL query: ' + e.message);
1038
- setRows([]);
1039
- setColumns([]);
1040
- return;
1041
- }
1042
- };
1043
- /**
1044
- * Render form fields based on the type of the node
1045
- * @param node the AST or subtree to render recursively
1046
- * @param keyPrefix a stringified version of the path from the root
1047
- *
1048
- * Note: The keyPrefix should be separated by '.' characters and each item
1049
- * should be a valid index into the node (eg. 'left.right.value' is a valid
1050
- * keyPrefix but 'left.args[0].value' is not -- should be 'left.args.0.value')
1051
- */
1052
- const renderNode = (node, keyPrefix = '') => {
1053
- const dateComparisonPartialMatch = formatDateComparisonNode(node);
1054
- switch (node.type) {
1055
- case 'binary_expr':
1056
- if (dateComparisonPartialMatch ||
1057
- (isDateTruncEquals(node) && client.databaseType !== 'BigQuery')) {
1058
- const { dateColumn, dateFilterType, intervalCount, intervalType, intervalPaths, } = getDateFilterInfo(node);
1059
- const isPlural = intervalCount !== 1 && dateFilterType !== 'in the current'
1060
- ? 's'
1061
- : '';
1062
- // Pull off the string literal date for "equals" comparisons
1063
- const rawDateStringEquals = node.right?.value ??
1064
- node.right?.args?.value[1]?.column ??
1065
- node.right?.args?.value[1]?.value;
1066
- const rawDateStringEqualsPath = (node.right?.value && 'node.right.value') ??
1067
- (node.right?.args?.value[1]?.column &&
1068
- 'node.right.args.value.1.column') ??
1069
- (node.right?.args?.value[1]?.value &&
1070
- 'node.right.args.value.1.value');
1071
- return (_jsxs("div", { style: { display: 'flex', gap: 20 }, children: [_jsx(SelectComponent, { value: dateColumn, onChange: (event) => {
1072
- const columnType = getColumnTypeByName(event.target.value);
1073
- if (isDateishColumnType(columnType)) {
1074
- // handleChange(value, keyPrefix + dateColumnPath, "text");
1075
- handleOperatorChange('IN_THE_LAST', node, keyPrefix, event.target.value);
1076
- }
1077
- else if (isNumericColumnType(columnType)) {
1078
- const newSubtree = deepCopy(defaultNumericComparison);
1079
- newSubtree.left.column = event.target.value;
1080
- handleReplaceSubtree(keyPrefix, newSubtree);
1081
- }
1082
- else if (isBoolColumnType(columnType)) {
1083
- const newSubtree = deepCopy(defaultBoolComparison);
1084
- newSubtree.left.column = event.target.value;
1085
- handleReplaceSubtree(keyPrefix, newSubtree);
1086
- }
1087
- else {
1088
- const newSubtree = deepCopy(defaultEntry);
1089
- newSubtree.left.args.value[0].column = event.target.value;
1090
- handleReplaceSubtree(keyPrefix, newSubtree);
1091
- }
1092
- }, options: getAllPossibleColumns().map((column) => ({
1093
- label: snakeAndCamelCaseToTitleCase(column.displayName),
1094
- value: column.name,
1095
- })), width: 200 }), _jsx(SelectComponent, { value: dateFilterType, onChange: (event) => {
1096
- if (event.target.value === dateFilterType)
1097
- return null;
1098
- let newSubtree = {};
1099
- // TODO: implement one for each database type (eg. pg, snowflake, etc.)
1100
- if (event.target.value === 'in the last') {
1101
- newSubtree = generateLastNPeriodsPostgres({
1102
- dateField: dateColumn,
1103
- intervalPeriod: `${intervalCount ?? 1} ${intervalType}`,
1104
- });
1105
- }
1106
- else if (event.target.value === 'in the previous') {
1107
- newSubtree = generatePreviousPeriodPostgres({
1108
- dateField: dateColumn,
1109
- intervalPeriod: `${intervalCount ?? 1} ${intervalType}`,
1110
- currentPeriod: intervalType,
1111
- });
1112
- }
1113
- else if (event.target.value === 'in the current') {
1114
- newSubtree = generateCurrentPeriodPostgres({
1115
- dateField: dateColumn,
1116
- currentPeriod: intervalType,
1117
- });
1118
- }
1119
- else if (event.target.value === 'equals') {
1120
- newSubtree = generateEqualsPostgres({
1121
- dateField: dateColumn,
1122
- currentPeriod: intervalType,
1123
- timestamp: '2024-01-01',
1124
- });
1125
- }
1126
- // replace the entire subtree for this filter
1127
- handleReplaceSubtree(keyPrefix, newSubtree);
1128
- }, options: [
1129
- { label: 'in the last', value: 'in the last' },
1130
- { label: 'in the previous', value: 'in the previous' },
1131
- { label: 'in the current', value: 'in the current' },
1132
- { label: 'equals', value: 'equals' },
1133
- ], width: 200 }), !['in the current', 'equals'].includes(dateFilterType) && (_jsx(TextInputComponent, { id: "date_filter_interval_count", value: intervalCount?.toString() ?? '', width: 70, onChange: (e) => {
1134
- if (Number.isNaN(parseFloat(e.target.value || '0'))) {
1135
- alert('Please input a number.');
1136
- return;
1137
- }
1138
- const isPluralNow = parseFloat(e.target.value || '0') !== 1 ? 's' : '';
1139
- intervalPaths.forEach((intervalPath) => handleChangeText([
1140
- {
1141
- value: `${e.target.value || 0} ${intervalType}${isPluralNow}`,
1142
- path: keyPrefix + intervalPath,
1143
- },
1144
- ]));
1145
- } })), _jsx(SelectComponent, { value: intervalType, onChange: (event) => {
1146
- if (intervalPaths.length === 1 &&
1147
- dateFilterType !== 'in the previous') {
1148
- handleChangeText([
1149
- {
1150
- value: intervalCount !== null
1151
- ? `${intervalCount} ${event.target.value}${isPlural}`
1152
- : event.target.value,
1153
- path: keyPrefix + intervalPaths[0],
1154
- },
1155
- ]);
1156
- return;
1157
- }
1158
- let newSubtree;
1159
- if (dateFilterType === 'in the previous') {
1160
- newSubtree = generatePreviousPeriodPostgres({
1161
- dateField: dateColumn,
1162
- intervalPeriod: `${intervalCount ?? 1} ${event.target.value}`,
1163
- currentPeriod: event.target.value,
1164
- });
1165
- }
1166
- else if (dateFilterType === 'equals') {
1167
- newSubtree = generateEqualsPostgres({
1168
- dateField: dateColumn,
1169
- currentPeriod: event.target.value,
1170
- timestamp: rawDateStringEquals,
1171
- });
1172
- }
1173
- else {
1174
- newSubtree = generateCurrentPeriodPostgres({
1175
- dateField: dateColumn,
1176
- currentPeriod: event.target.value,
1177
- });
1178
- }
1179
- handleReplaceSubtree(keyPrefix, newSubtree);
1180
- }, options: dateFilterType === 'in the previous' ||
1181
- dateFilterType === 'in the last'
1182
- ? [
1183
- { label: `year${isPlural}`, value: 'year' },
1184
- { label: `month${isPlural}`, value: 'month' },
1185
- { label: `week${isPlural}`, value: 'week' },
1186
- { label: `day${isPlural}`, value: 'day' },
1187
- { label: `hour${isPlural}`, value: 'hour' },
1188
- ]
1189
- : [
1190
- { label: `year${isPlural}`, value: 'year' },
1191
- { label: `quarter${isPlural}`, value: 'quarter' },
1192
- { label: `month${isPlural}`, value: 'month' },
1193
- { label: `week${isPlural}`, value: 'week' },
1194
- { label: `day${isPlural}`, value: 'day' },
1195
- { label: `hour${isPlural}`, value: 'hour' },
1196
- ], width: 200 }), dateFilterType === 'equals' && (_jsx(TextInputComponent, { id: "date_filter_equals_raw_date", value: rawDateStringEquals, width: 120, onChange: (e) => {
1197
- handleChangeText([
1198
- {
1199
- value: e.target.value,
1200
- path: keyPrefix + rawDateStringEqualsPath,
1201
- },
1202
- ]);
1203
- } }))] }));
1204
- }
1205
- else if (isInTheLastInterval(node, client.databaseType)) {
1206
- const { dateColumn } = getDateFilterInfo(node);
1207
- const options = getAllPossibleColumns().map((column) => ({
1208
- label: snakeAndCamelCaseToTitleCase(column.displayName),
1209
- value: column.name,
1210
- }));
1211
- const plural = node.right.args.value[1].expr.value !== 1 ? 's' : '';
1212
- return (_jsxs("div", { style: {
1213
- display: 'flex',
1214
- flexDirection: 'row',
1215
- alignItems: 'center',
1216
- gap: 20,
1217
- }, children: [_jsx(SelectComponent, { value: node.left.column, onChange: (event) => {
1218
- const columnType = getColumnTypeByName(event.target.value);
1219
- if (isDateishColumnType(columnType)) {
1220
- // handleChange(value, keyPrefix + dateColumnPath, "text");
1221
- handleOperatorChange('IN_THE_LAST', node, keyPrefix, event.target.value);
1222
- }
1223
- else if (isNumericColumnType(columnType)) {
1224
- const newSubtree = deepCopy(defaultNumericComparison);
1225
- newSubtree.left.column = event.target.value;
1226
- handleReplaceSubtree(keyPrefix, newSubtree);
1227
- }
1228
- else if (isBoolColumnType(columnType)) {
1229
- const newSubtree = deepCopy(defaultBoolComparison);
1230
- newSubtree.left.column = event.target.value;
1231
- handleReplaceSubtree(keyPrefix, newSubtree);
1232
- }
1233
- else {
1234
- const newSubtree = deepCopy(defaultEntry);
1235
- newSubtree.left.args.value[0].column = event.target.value;
1236
- handleReplaceSubtree(keyPrefix, newSubtree);
1237
- }
1238
- }, options: options, width: 200 }), _jsx(SelectComponent, { value: 'IN_THE_LAST', onChange: (event) => {
1239
- handleOperatorChange(event.target.value, node, keyPrefix, dateColumn);
1240
- }, options: [
1241
- { label: 'in the last', value: 'IN_THE_LAST' },
1242
- { label: 'in the previous', value: 'IN_THE_PREVIOUS' },
1243
- { label: 'in the current', value: 'IN_THE_CURRENT' },
1244
- { label: 'equals', value: 'EQUALS' },
1245
- { label: 'is not null', value: 'IS NOT' },
1246
- { label: 'is null', value: 'IS' },
1247
- ], width: 200 }), _jsx(TextInputComponent, { id: 'date_window_interval_count', value: node.right.args.value[1].expr.value, width: 120, onChange: (e) => {
1248
- handleChange([
1249
- {
1250
- value: e.target.value,
1251
- path: keyPrefix + 'right.args.value||1.expr.value',
1252
- },
1253
- ]);
1254
- } }), _jsx("div", { children: _jsx(SelectComponent, { value: node.right.args.value[1].unit, onChange: (event) => handleChange([
1255
- {
1256
- value: event.target.value,
1257
- path: keyPrefix + 'right.args.value||1.unit',
1258
- },
1259
- ]), options: [
1260
- { label: `year${plural}`, value: '* 365 DAY' },
1261
- { label: `month${plural}`, value: '* 30 DAY' },
1262
- { label: `week${plural}`, value: '* 7 DAY' },
1263
- { label: `day${plural}`, value: 'DAY' },
1264
- ], width: 200 }) })] }));
1265
- }
1266
- else if (isTheCurrentInterval(node, client.databaseType)) {
1267
- const options = getAllPossibleColumns().map((column) => ({
1268
- label: snakeAndCamelCaseToTitleCase(column.displayName),
1269
- value: column.name,
1270
- }));
1271
- return (_jsxs("div", { style: {
1272
- display: 'flex',
1273
- flexDirection: 'row',
1274
- alignItems: 'center',
1275
- gap: 20,
1276
- }, children: [_jsx(SelectComponent, { value: node.left.column, onChange: (event) => {
1277
- const columnType = getColumnTypeByName(event.target.value);
1278
- if (isDateishColumnType(columnType)) {
1279
- // handleChange(value, keyPrefix + dateColumnPath, "text");
1280
- handleOperatorChange('IN_THE_LAST', node, keyPrefix, event.target.value);
1281
- }
1282
- else if (isNumericColumnType(columnType)) {
1283
- const newSubtree = deepCopy(defaultNumericComparison);
1284
- newSubtree.left.column = event.target.value;
1285
- handleReplaceSubtree(keyPrefix, newSubtree);
1286
- }
1287
- else if (isBoolColumnType(columnType)) {
1288
- const newSubtree = deepCopy(defaultBoolComparison);
1289
- newSubtree.left.column = event.target.value;
1290
- handleReplaceSubtree(keyPrefix, newSubtree);
1291
- }
1292
- else {
1293
- const newSubtree = deepCopy(defaultEntry);
1294
- newSubtree.left.args.value[0].column = event.target.value;
1295
- handleReplaceSubtree(keyPrefix, newSubtree);
1296
- }
1297
- }, options: options, width: 200 }), _jsx(SelectComponent, { value: 'IN_THE_CURRENT', onChange: (event) => {
1298
- handleOperatorChange(event.target.value, node, keyPrefix, node.left.column);
1299
- }, options: [
1300
- { label: 'in the last', value: 'IN_THE_LAST' },
1301
- { label: 'in the previous', value: 'IN_THE_PREVIOUS' },
1302
- { label: 'in the current', value: 'IN_THE_CURRENT' },
1303
- { label: 'equals', value: 'EQUALS' },
1304
- { label: 'is not null', value: 'IS NOT' },
1305
- { label: 'is null', value: 'IS' },
1306
- // { label: 'equals', value: 'equals' },
1307
- ], width: 200 }), _jsx(SelectComponent, { value: node.left.args.value[1].column, onChange: (event) => {
1308
- handleChange([
1309
- {
1310
- value: event.target.value,
1311
- path: 'right.args.value||1.column',
1312
- },
1313
- {
1314
- value: event.target.value,
1315
- path: 'left.args.value||1.column',
1316
- },
1317
- ]);
1318
- }, options: [
1319
- { label: `year`, value: 'YEAR' },
1320
- { label: `quarter`, value: 'QUARTER' },
1321
- { label: `month`, value: 'MONTH' },
1322
- { label: `week`, value: 'WEEK' },
1323
- ], width: 200 })] }));
1324
- }
1325
- else if (isThePreviousInterval(node)) {
1326
- const options = getAllPossibleColumns().map((column) => ({
1327
- label: snakeAndCamelCaseToTitleCase(column.displayName),
1328
- value: column.name,
1329
- }));
1330
- return (_jsxs("div", { style: {
1331
- display: 'flex',
1332
- flexDirection: 'row',
1333
- alignItems: 'center',
1334
- gap: 20,
1335
- }, children: [_jsx(SelectComponent, { value: node.left.column, onChange: (event) => {
1336
- const columnType = getColumnTypeByName(event.target.value);
1337
- if (isDateishColumnType(columnType)) {
1338
- // handleChange(value, keyPrefix + dateColumnPath, "text");
1339
- handleOperatorChange('IN_THE_LAST', node, keyPrefix, event.target.value);
1340
- }
1341
- else if (isNumericColumnType(columnType)) {
1342
- const newSubtree = deepCopy(defaultNumericComparison);
1343
- newSubtree.left.column = event.target.value;
1344
- handleReplaceSubtree(keyPrefix, newSubtree);
1345
- }
1346
- else if (isBoolColumnType(columnType)) {
1347
- const newSubtree = deepCopy(defaultBoolComparison);
1348
- newSubtree.left.column = event.target.value;
1349
- handleReplaceSubtree(keyPrefix, newSubtree);
1350
- }
1351
- else {
1352
- const newSubtree = deepCopy(defaultEntry);
1353
- newSubtree.left.args.value[0].column = event.target.value;
1354
- handleReplaceSubtree(keyPrefix, newSubtree);
1355
- }
1356
- }, options: options, width: 200 }), _jsx(SelectComponent, { value: 'IN_THE_PREVIOUS', onChange: (event) => {
1357
- handleOperatorChange(event.target.value, node, keyPrefix, node.left.column);
1358
- }, options: [
1359
- { label: 'in the last', value: 'IN_THE_LAST' },
1360
- { label: 'in the previous', value: 'IN_THE_PREVIOUS' },
1361
- { label: 'in the current', value: 'IN_THE_CURRENT' },
1362
- { label: 'equals', value: 'EQUALS' },
1363
- { label: 'is not null', value: 'IS NOT' },
1364
- { label: 'is null', value: 'IS' },
1365
- // { label: 'equals', value: 'equals' },
1366
- ], width: 200 }), _jsx(SelectComponent, { value: node.left.args.value[1].column, onChange: (event) => {
1367
- const dayConversion = {
1368
- YEAR: 365,
1369
- QUARTER: 90,
1370
- MONTH: 30,
1371
- WEEK: 7,
1372
- };
1373
- handleChange([
1374
- {
1375
- value: event.target.value,
1376
- path: 'left.args.value||1.column',
1377
- },
1378
- {
1379
- value: event.target.value,
1380
- path: 'right.args.value||1.column',
1381
- },
1382
- {
1383
- value: dayConversion[event.target.value] || 30,
1384
- path: 'right.args.value||0.args.value||1.expr.value',
1385
- },
1386
- ]);
1387
- }, options: [
1388
- { label: `year`, value: 'YEAR' },
1389
- { label: `quarter`, value: 'QUARTER' },
1390
- { label: `month`, value: 'MONTH' },
1391
- { label: `week`, value: 'WEEK' },
1392
- ], width: 200 })] }));
1393
- }
1394
- else if (isEquals(node, client.databaseType)) {
1395
- const options = getAllPossibleColumns().map((column) => ({
1396
- label: snakeAndCamelCaseToTitleCase(column.displayName),
1397
- value: column.name,
1398
- }));
1399
- return (_jsxs("div", { style: {
1400
- display: 'flex',
1401
- flexDirection: 'row',
1402
- alignItems: 'center',
1403
- gap: 20,
1404
- }, children: [_jsx(SelectComponent, { value: node.left.column, onChange: (event) => {
1405
- const columnType = getColumnTypeByName(event.target.value);
1406
- if (isDateishColumnType(columnType)) {
1407
- // handleChange(value, keyPrefix + dateColumnPath, "text");
1408
- handleOperatorChange('IN_THE_LAST', node, keyPrefix, event.target.value);
1409
- }
1410
- else if (isNumericColumnType(columnType)) {
1411
- const newSubtree = deepCopy(defaultNumericComparison);
1412
- newSubtree.left.column = event.target.value;
1413
- handleReplaceSubtree(keyPrefix, newSubtree);
1414
- }
1415
- else if (isBoolColumnType(columnType)) {
1416
- const newSubtree = deepCopy(defaultBoolComparison);
1417
- newSubtree.left.column = event.target.value;
1418
- handleReplaceSubtree(keyPrefix, newSubtree);
1419
- }
1420
- else {
1421
- const newSubtree = deepCopy(defaultEntry);
1422
- newSubtree.left.args.value[0].column = event.target.value;
1423
- handleReplaceSubtree(keyPrefix, newSubtree);
1424
- }
1425
- }, options: options, width: 200 }), _jsx(SelectComponent, { value: 'EQUALS', onChange: (event) => {
1426
- handleOperatorChange(event.target.value, node, keyPrefix, node.left.column);
1427
- }, options: [
1428
- { label: 'in the last', value: 'IN_THE_LAST' },
1429
- { label: 'in the previous', value: 'IN_THE_PREVIOUS' },
1430
- { label: 'in the current', value: 'IN_THE_CURRENT' },
1431
- { label: 'equals', value: 'EQUALS' },
1432
- { label: 'is not null', value: 'IS NOT' },
1433
- { label: 'is null', value: 'IS' },
1434
- // { label: 'equals', value: 'equals' },
1435
- ], width: 200 }), _jsx(SelectComponent, { value: node.right.args.value[1].column, onChange: (event) => {
1436
- handleChange([
1437
- {
1438
- value: event.target.value,
1439
- path: 'right.args.value||1.column',
1440
- },
1441
- {
1442
- value: event.target.value,
1443
- path: 'left.args.value||1.column',
1444
- },
1445
- ]);
1446
- }, options: [
1447
- { label: `year`, value: 'YEAR' },
1448
- { label: `quarter`, value: 'QUARTER' },
1449
- { label: `month`, value: 'MONTH' },
1450
- { label: `week`, value: 'WEEK' },
1451
- ], width: 200 }), _jsx(TextInputComponent, { id: 'quoted_string', value: node.right.args.value[0].value, width: 120, onChange: (e) => handleChange([
1452
- {
1453
- value: e.target.value,
1454
- path: 'right.args.value||0.value',
1455
- },
1456
- ]) })] }));
1457
- }
1458
- else if (isColumnComparison(node)) {
1459
- const options = getAllPossibleColumns().map((column) => ({
1460
- label: snakeAndCamelCaseToTitleCase(column.displayName),
1461
- value: column.name,
1462
- }));
1463
- // grab the value of the left child of the column comparison
1464
- // operator (ie. the column name)
1465
- const leftChildValue = getColumnValueForColumnComparison(node);
1466
- const tables = getTableNames(baseAst);
1467
- const table = tables.length === 1 ? tables[0] : initialTableName;
1468
- const column = schema
1469
- .find((tableInfo) => tableInfo.name === table)
1470
- ?.columns.find((col) => col.name === leftChildValue);
1471
- const columnType = column?.fieldType;
1472
- const operatorOptions = [
1473
- ...(isNumericColumnType(columnType)
1474
- ? [
1475
- { label: 'equal to', value: '=' },
1476
- { label: 'not equal to', value: '!=' },
1477
- { label: 'greater than', value: '>' },
1478
- { label: 'less than', value: '<' },
1479
- { label: 'greater than or equal to', value: '>=' },
1480
- { label: 'less than or equal to', value: '<=' },
1481
- { label: 'is not null', value: 'IS NOT' },
1482
- { label: 'is null', value: 'IS' },
1483
- ]
1484
- : []),
1485
- ...(isTextColumnType(columnType)
1486
- ? [
1487
- { label: 'is exactly', value: 'LIKE' },
1488
- { label: 'is not exactly', value: 'NOT LIKE' },
1489
- { label: 'is', value: 'IN' },
1490
- { label: 'is not', value: 'NOT IN' },
1491
- { label: 'is not null', value: 'IS NOT' },
1492
- { label: 'is null', value: 'IS' },
1493
- ]
1494
- : []),
1495
- ...(isDateishColumnType(columnType)
1496
- ? [
1497
- { label: 'in the last', value: 'IN_THE_LAST' },
1498
- {
1499
- label: 'in the previous',
1500
- value: 'IN_THE_PREVIOUS',
1501
- },
1502
- { label: 'in the current', value: 'IN_THE_CURRENT' },
1503
- { label: 'equals', value: 'equals' },
1504
- ]
1505
- : []),
1506
- ];
1507
- return (_jsxs("div", { style: {
1508
- display: 'flex',
1509
- gap: 12,
1510
- flexDirection: 'column',
1511
- width: '100%',
1512
- padding: '6px 0px',
1513
- }, children: [_jsxs("div", { style: {
1514
- display: 'flex',
1515
- gap: 20,
1516
- flexDirection: showNodeAsRow(node, formData)
1517
- ? 'row'
1518
- : 'column',
1519
- width: '100%',
1520
- }, children: [_jsx(SelectComponent, { value: leftChildValue, onChange: (event) => {
1521
- const columnType = getColumnTypeByName(event.target.value);
1522
- if (isDateishColumnType(columnType)) {
1523
- handleOperatorChange('IN_THE_LAST', node, keyPrefix, event.target.value);
1524
- }
1525
- else if (isNumericColumnType(columnType)) {
1526
- const newSubtree = deepCopy(defaultNumericComparison);
1527
- newSubtree.left.column = event.target.value;
1528
- handleReplaceSubtree(keyPrefix, newSubtree);
1529
- }
1530
- else if (isBoolColumnType(columnType)) {
1531
- const newSubtree = deepCopy(defaultBoolComparison);
1532
- newSubtree.left.column = event.target.value;
1533
- handleReplaceSubtree(keyPrefix, newSubtree);
1534
- }
1535
- else {
1536
- const newSubtree = deepCopy(defaultEntry);
1537
- newSubtree.left.args.value[0].column = event.target.value;
1538
- handleReplaceSubtree(keyPrefix, newSubtree);
1539
- }
1540
- }, options: options, width: 200 }), operatorOptions.length > 0 && (_jsx(SelectComponent, { value: node.operator, onChange: (event) => {
1541
- handleOperatorChange(event.target.value, node, keyPrefix, leftChildValue);
1542
- }, options: operatorOptions, width: 200 })), node.right &&
1543
- node.right.type !== 'expr_list' &&
1544
- renderNode(node.right, keyPrefix + 'right.')] }, keyPrefix), node.right && node.right.type === 'expr_list' && (_jsx("div", { style: {
1545
- display: 'grid',
1546
- gridTemplateColumns: 'repeat(2, 1fr)',
1547
- gap: 12,
1548
- }, children: uniqueValues[table] &&
1549
- Object.keys(uniqueValues[table][leftChildValue] ?? {}).map((key) => (_jsx(CheckboxComponent, { label: key, isChecked: uniqueValues[table][leftChildValue][key], onChange: (event) => {
1550
- const newValues = deepCopy(uniqueValues);
1551
- newValues[table][leftChildValue][key] =
1552
- event.target.checked;
1553
- setUniqueValues(newValues);
1554
- if (event.target.checked) {
1555
- handleInsertVariant(keyPrefix + 'right.' + 'value', key);
1556
- }
1557
- else {
1558
- handleDeleteVariant(keyPrefix + 'right.' + 'value', key);
1559
- }
1560
- } }))) }, keyPrefix + 'right.'))] }));
1561
- }
1562
- else {
1563
- const columnName = node.left.column;
1564
- const column = schema
1565
- .find((tableInfo) => tableInfo.name === currentTable)
1566
- ?.columns.find((col) => col.name === columnName);
1567
- const columnType = column?.fieldType;
1568
- return (_jsxs("div", { style: {
1569
- display: 'flex',
1570
- gap: 12,
1571
- justifyContent: 'space-between',
1572
- flexDirection: showNodeAsRow(node, formData) ? 'row' : 'column',
1573
- width: '100%',
1574
- }, children: [node.left && renderNode(node.left, keyPrefix + 'left.'), _jsx(SelectComponent, { value: node.operator, onChange: (event) => {
1575
- handleOperatorChange(event.target.value, node, keyPrefix);
1576
- }, options: [
1577
- // { label: `and`, value: "AND" },
1578
- // { label: `or`, value: "OR" },
1579
- ...(isNumericColumnType(columnType)
1580
- ? [
1581
- { label: 'equal to', value: '=' },
1582
- { label: 'not equal to', value: '!=' },
1583
- { label: 'greater than', value: '>' },
1584
- { label: 'less than', value: '<' },
1585
- { label: 'greater than or equal to', value: '>=' },
1586
- { label: 'less than or equal to', value: '<=' },
1587
- { label: 'is not null', value: 'IS NOT' },
1588
- { label: 'is null', value: 'IS' },
1589
- ]
1590
- : []),
1591
- ...(isTextColumnType(columnType)
1592
- ? [
1593
- { label: 'is exactly', value: 'LIKE' },
1594
- { label: 'is not exactly', value: 'NOT LIKE' },
1595
- { label: 'is', value: 'IN' },
1596
- { label: 'is not', value: 'NOT IN' },
1597
- { label: 'is not null', value: 'IS NOT' },
1598
- { label: 'is null', value: 'IS' },
1599
- ]
1600
- : []),
1601
- ...(isDateishColumnType(columnType)
1602
- ? [
1603
- { label: 'in the last', value: 'IN_THE_LAST' },
1604
- { label: 'in the previous', value: 'IN_THE_PREVIOUS' },
1605
- { label: 'in the current', value: 'IN_THE_CURRENT' },
1606
- { label: 'equals', value: 'EQUALS' },
1607
- { label: 'is not null', value: 'IS NOT' },
1608
- { label: 'is null', value: 'IS' },
1609
- ]
1610
- : []),
1611
- // { label: `minus`, value: "-" },
1612
- // { label: `plus`, value: "+" },
1613
- ], width: 200 }), node.right && renderNode(node.right, keyPrefix + 'right.')] }, keyPrefix));
1614
- }
1615
- case 'column_ref': {
1616
- const options = getAllPossibleColumns().map((column) => ({
1617
- label: snakeAndCamelCaseToTitleCase(column.displayName),
1618
- value: column.name,
1619
- }));
1620
- return (_jsx(SelectComponent, { value: node.column ?? options[0]?.value, onChange: (event) => {
1621
- handleChange([
1622
- { value: event.target.value, path: keyPrefix + 'column' },
1623
- ]);
1624
- }, options: options, width: 200 }));
1625
- }
1626
- case 'expr_list': {
1627
- const len = node.value.length;
1628
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'row', gap: 12 }, children: [node.value.map((elem, index) => {
1629
- if (elem.value) {
1630
- return (_jsx(TextInputComponent, { id: `expr_list_${index}`, width: 200, value: elem.value, onChange: (e) => handleChange([
1631
- {
1632
- value: e.target.value,
1633
- path: keyPrefix + `value.${index}.`,
1634
- },
1635
- ]) }, `input_${index}`));
1636
- }
1637
- return renderNode(elem, keyPrefix + `value.${index}.`);
1638
- }), len > 1 && (_jsx(SecondaryButtonComponent, { label: '-', onClick: () => handleDeleteVariant(keyPrefix + 'value') })), _jsx(SecondaryButtonComponent, { onClick: () => handleInsertVariant(keyPrefix + 'value'), label: '+' })] }, keyPrefix));
1639
- }
1640
- case 'double_quote_string':
1641
- case 'single_quote_string':
1642
- return (_jsx(TextInputComponent, { id: 'quoted_string', value: node.value.replaceAll('%', ''), width: 120, onChange: (e) => handleChange([
1643
- {
1644
- value: e.target.value,
1645
- path: keyPrefix + 'value',
1646
- },
1647
- ]) }));
1648
- case 'null':
1649
- return _jsx("div", {});
1650
- case 'number':
1651
- return (_jsx(TextInputComponent, { id: "quill_number_input", value: node.value, width: 120, onChange: (e) => {
1652
- handleChange([
1653
- {
1654
- value: e.target.value,
1655
- path: keyPrefix + 'value',
1656
- },
1657
- ]);
1658
- } }));
1659
- case 'bool':
1660
- return (_jsx(SelectComponent, { value: node.value.toString(), onChange: (event) => {
1661
- let formatted = true;
1662
- if (event.target.value === 'false') {
1663
- formatted = false;
1664
- }
1665
- handleChange([
1666
- {
1667
- value: formatted,
1668
- path: keyPrefix + 'value',
1669
- },
1670
- ]);
1671
- }, options: [
1672
- { label: 'is true', value: 'true' },
1673
- { label: 'is false', value: 'false' },
1674
- ], width: 200 }));
1675
- case 'function':
1676
- if (!node.args) {
1677
- return _jsx("label", {});
1678
- }
1679
- else if (node.args.type === 'expr_list' &&
1680
- node.args.value.length === 1) {
1681
- return (_jsx("div", { style: { display: 'flex', flexDirection: 'row' }, children: node.args.value[0] &&
1682
- renderNode(node.args.value[0], keyPrefix + 'args.value.0.') }));
1683
- }
1684
- else if (node.args.type === 'expr_list' &&
1685
- node.args.value.length === 2) {
1686
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'row', gap: 20 }, children: [node.args.value[0] &&
1687
- renderNode(node.args.value[0], keyPrefix + 'args.value.0.'), node.args.value[1] &&
1688
- renderNode(node.args.value[1], keyPrefix + 'args.value.1.')] }));
1689
- }
1690
- return node.name;
1691
- case 'interval':
1692
- return (_jsx("div", { style: { display: 'flex', flexDirection: 'row', gap: 20 }, children: renderNode(node.expr, keyPrefix + 'expr.') }));
1693
- default:
1694
- return null;
1695
- }
1696
- };
1697
- const isValidPivot = (fields) => {
1698
- let validPivot = true;
1699
- if (pivot.rowField &&
1700
- !fields.find((field) => field.name === pivot.rowField)) {
1701
- validPivot = false;
1702
- }
1703
- if (pivot.valueField &&
1704
- !fields.find((field) => field.name === pivot.valueField)) {
1705
- validPivot = false;
1706
- }
1707
- if (pivot.columnField &&
1708
- !fields.find((field) => field.name === pivot.columnField)) {
1709
- validPivot = false;
1710
- }
1711
- return validPivot;
1712
- };
1713
- /**
1714
- * @param filterTree
1715
- * Returns a list of filters to be displayed
1716
- * Replaces the functionality of renderNodes in the context of filters
1717
- */
1718
- const renderFilters = (filterTree) => {
1719
- let tree = filterTree;
1720
- let filterStack = [];
1721
- /**
1722
- * Function that takes in a FilterTree and flattens it into an array using in order traversal
1723
- */
1724
- function traverseTree(node) {
1725
- if (!node) {
1726
- return;
1727
- }
1728
- if (node.leaf) {
1729
- filterStack.push(node);
1730
- }
1731
- else {
1732
- traverseTree(node.leftNode);
1733
- filterStack.push(node);
1734
- traverseTree(node.rightNode);
1735
- }
1736
- }
1737
- /**
1738
- * Given an array of Filters (presumed to be in in-order state), generate
1739
- * the corresponding Filter tree. Essentially the reverse of what traverseTree does
1740
- */
1741
- function filterStackToFilterTree(stack) {
1742
- function buildTree(i) {
1743
- const newNode = {
1744
- leaf: false,
1745
- operator: null,
1746
- leftNode: null,
1747
- rightNode: null,
1748
- };
1749
- if (i >= stack.length) {
1750
- return null;
1751
- }
1752
- else if (stack[i].leaf) {
1753
- if (i < stack.length - 1) {
1754
- // more nodes later
1755
- newNode.operator = stack[i + 1].operator;
1756
- newNode.leftNode = {
1757
- leaf: true,
1758
- leftNode: null,
1759
- rightNode: null,
1760
- operator: null,
1761
- value: stack[i].value,
1762
- };
1763
- newNode.rightNode = buildTree(i + 2);
1764
- }
1765
- else {
1766
- newNode.leaf = true;
1767
- newNode.value = stack[i].value;
1768
- }
1769
- }
1770
- return newNode;
1771
- }
1772
- return buildTree(0);
1773
- }
1774
- traverseTree(tree);
1775
- // Remove null (invalid) filters from filter stack
1776
- filterStack = filterStack.filter((filter) => {
1777
- return ((!filter.leaf &&
1778
- filter.rightNode &&
1779
- (!filter.rightNode.leaf || filter.rightNode.value)) ||
1780
- (filter.leaf && filter.value));
1781
- });
1782
- // Render filterStack
1783
- return (_jsx("div", { style: {
1784
- display: 'flex',
1785
- flexDirection: 'column',
1786
- }, children: filterStack.map((item, index) => {
1787
- if (!item.leaf &&
1788
- (item.operator === 'and' || item.operator === 'or')) {
1789
- return (_jsx("div", { style: {
1790
- width: 'fit-content',
1791
- marginBottom: '8px',
1792
- marginTop: '8px',
1793
- }, children: _jsx(TabsComponent, { value: item.operator.toUpperCase(), options: DEFAULT_TAB_OPTIONS, onChange: () => {
1794
- if (item.operator === 'and') {
1795
- item.operator = 'or';
1796
- }
1797
- else {
1798
- item.operator = 'and';
1799
- }
1800
- let newFormData = null;
1801
- if (tree) {
1802
- newFormData = filterTreeToAst(tree, client.databaseType.toLowerCase());
1803
- }
1804
- const newAst = deepCopy({
1805
- ...defaultAST,
1806
- ...baseAst,
1807
- ...(!baseAst?.columns && {
1808
- columns: getAllPossibleColumns().map((c) => {
1809
- const newColumn = deepCopy(defaultColumn);
1810
- newColumn.expr.column = c.name;
1811
- return newColumn;
1812
- }),
1813
- }),
1814
- ...(!baseAst?.from && {
1815
- from: [{ ...defaultTable, table: initialTableName }],
1816
- }),
1817
- where: newFormData,
1818
- });
1819
- setBaseAst(newAst);
1820
- setFormData(newFormData);
1821
- fetchSqlQuery(newAst, newFormData);
1822
- } }) }, index));
1823
- }
1824
- else {
1825
- return (_jsx(FilterPopoverWrapper, { schema: schema.find((s) => s.name === currentTable || s.displayName === currentTable) ?? schema[0], filter: item.value, filterLabel: item.value ? filterSentence(item.value) : '', index: index, FilterTagComponent: FilterPopoverComponent, FilterModal: FilterModal, fieldValuesMap: fieldValuesMap, ButtonComponent: ButtonComponent, SecondaryButtonComponent: SecondaryButtonComponent, SelectComponent: SelectComponent, TextInputComponent: TextInputComponent, MultiSelectComponent: MultiSelectComponent, handleFilterSave: (filter) => {
1826
- item.value = filter;
1827
- let newFormData = null;
1828
- if (tree) {
1829
- newFormData = filterTreeToAst(tree, client.databaseType.toLowerCase());
1830
- }
1831
- const newAst = deepCopy({
1832
- ...defaultAST,
1833
- ...baseAst,
1834
- ...(!baseAst?.columns && {
1835
- columns: getAllPossibleColumns().map((c) => {
1836
- const newColumn = deepCopy(defaultColumn);
1837
- newColumn.expr.column = c.name;
1838
- return newColumn;
1839
- }),
1840
- }),
1841
- ...(!baseAst?.from && {
1842
- from: [{ ...defaultTable, table: initialTableName }],
1843
- }),
1844
- where: newFormData,
1845
- });
1846
- setBaseAst(newAst);
1847
- setFormData(newFormData);
1848
- fetchSqlQuery(newAst, newFormData);
1849
- }, handleFilterDelete: (i) => {
1850
- if (i > 0) {
1851
- filterStack.splice(i - 1, 2);
1852
- }
1853
- else {
1854
- if (filterStack.length > 1) {
1855
- filterStack.splice(i, 2);
1856
- }
1857
- else {
1858
- filterStack.splice(i, 1);
1859
- }
1860
- }
1861
- tree = filterStackToFilterTree(filterStack);
1862
- let newFormData = null;
1863
- if (tree) {
1864
- newFormData = filterTreeToAst(tree, client.databaseType.toLowerCase());
1865
- }
1866
- const newAst = deepCopy({
1867
- ...defaultAST,
1868
- ...baseAst,
1869
- ...(!baseAst?.columns && {
1870
- columns: getAllPossibleColumns().map((c) => {
1871
- const newColumn = deepCopy(defaultColumn);
1872
- newColumn.expr.column = c.name;
1873
- return newColumn;
1874
- }),
1875
- }),
1876
- ...(!baseAst?.from && {
1877
- from: [{ ...defaultTable, table: initialTableName }],
1878
- }),
1879
- where: newFormData,
1880
- });
1881
- setBaseAst(newAst);
1882
- setFormData(newFormData);
1883
- fetchSqlQuery(newAst, newFormData);
1884
- } }, 'filter' + index));
1885
- }
1886
- }) }));
1887
- };
1888
- const getAllPossibleColumns = () => {
1889
- if (!baseAst || !baseAst.from) {
1890
- return schema.flatMap((table) => table.columns.map((c) => ({
1891
- ...c,
1892
- table: table.displayName,
1893
- })));
1894
- }
1895
- // TODO: support infinitely nested FROM table lookups.
1896
- // This currently only supports top-level table names in the FROM section
1897
- // of queries (eg. FROM "table_name", not "FROM (SELECT * FROM other) AS table_name")
1898
- const tableNamesInQuery = baseAst.from.map((tbl) => tbl.table);
1899
- return schema
1900
- .filter((t) => tableNamesInQuery.includes(t.displayName))
1901
- .flatMap((table) => table.columns
1902
- .map((c) => ({
1903
- ...c,
1904
- table: table.displayName,
1905
- }))
1906
- .sort((a, b) => {
1907
- const aIsId = a.name.toLowerCase() === 'id' ||
1908
- a.name.toLowerCase().endsWith('_id') ||
1909
- a.name.endsWith('Id');
1910
- const bIsId = b.name.toLowerCase() === 'id' ||
1911
- b.name.toLowerCase().endsWith('_id') ||
1912
- b.name.endsWith('Id');
1913
- if (aIsId && !bIsId)
1914
- return 1;
1915
- if (bIsId && !aIsId)
1916
- return -1;
1917
- return 0;
1918
- }));
1919
- };
1920
- /**
1921
- * Return whether all columns have been selected (used to hide select all
1922
- * and show clear button).
1923
- */
1924
- const isSelectedAllColumns = () => {
1925
- if (selectedColumns.length < 1)
1926
- return false;
1927
- const allColumns = orderedColumnNames.filter((row) => {
1928
- const [table] = row.split('.');
1929
- const selectedTable = selectedColumns[0].split('.')[0];
1930
- return selectedTable === table;
1931
- });
1932
- return selectedColumns.length === allColumns.length;
1933
- };
1934
- const nameToColumn = (name) => ({
1935
- type: 'expr',
1936
- expr: {
1937
- type: 'column_ref',
1938
- table: null,
1939
- column: name,
1940
- },
1941
- as: null,
1942
- });
1943
- const AddConditionPopover = ({ onSave }) => {
1944
- return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 2 }, children: [_jsx("h1", { style: {
1945
- fontWeight: '600',
1946
- fontSize: 18,
1947
- margin: 0,
1948
- textAlign: 'left',
1949
- }, children: "Add condition" }), _jsx(TabsComponent, { value: topLevelBinaryOperator, options: DEFAULT_TAB_OPTIONS, onChange: (event) => setTopLevelBinaryOperator(event.target.value) }), activeEditItem && renderNode(activeEditItem), _jsx("div", { style: {
1950
- display: 'flex',
1951
- flexDirection: 'row',
1952
- gap: 8,
1953
- justifyContent: 'end',
1954
- }, children: _jsx(ButtonComponent, { onClick: onSave, label: 'Add condition' }) })] }));
1955
- };
1956
- const fetchUponChange = async (baseAst, newFormData) => {
1957
- // if newFormData is null still use it
1958
- const curFormData = newFormData !== undefined ? newFormData : formData;
1959
- let rows;
1960
- if ((curFormData || baseAst) && !loading) {
1961
- try {
1962
- setLoading(true);
1963
- const hostedBody = {
1964
- metadata: {
1965
- clientId: client.publicKey,
1966
- ast: { ...baseAst, where: curFormData },
1967
- publicKey: client.publicKey,
1968
- orgId: client.customerId,
1969
- task: 'patterns',
1970
- getCustomFields: false,
1971
- customFields,
1972
- additionalProcessing: { page: { currentPage: 0, rowsPerPage: 20 } },
1973
- useUpdatedDataGathering: true,
1974
- useNewNodeSql: true, // new flag
1975
- },
1976
- };
1977
- const tables = getTableNames(baseAst);
1978
- const table = tables.length >= 1 ? tables[0] : initialTableName;
1979
- let newUniqueValues = uniqueValues;
1980
- let dateRangesTemp = dateRanges;
1981
- let curReport = tempReport;
1982
- if ((newUniqueValues && Object.keys(newUniqueValues).length === 0) ||
1983
- table !== currentTable) {
1984
- const tableInfo = schema.find((tableInfo) => tableInfo.name === table);
1985
- if (tableInfo) {
1986
- newUniqueValues = await getUniqueStringValues(tableInfo.columns, table);
1987
- if (hashCode(uniqueValues) !== hashCode(newUniqueValues)) {
1988
- setUniqueValues(newUniqueValues);
1989
- }
1990
- dateRangesTemp = await getDateRanges(tableInfo.columns, table);
1991
- setDateRanges(dateRangesTemp || {});
1992
- }
1993
- setCurrentTable(table);
1994
- }
1995
- const cloudBody = {};
1996
- const data2 = await getData(client, 'dashquery', 'same-origin', hostedBody, cloudBody);
1997
- if (data2.success === false) {
1998
- throw new Error(data2.errorMessage);
1999
- }
2000
- if (client.databaseType &&
2001
- client.databaseType.toLowerCase() === 'bigquery') {
2002
- parseValueFromBigQueryDates(data2.rows, columns);
2003
- }
2004
- rows = data2.rows;
2005
- if (data2.rowCount) {
2006
- setNumberOfRows(data2.rowCount);
2007
- }
2008
- if (data2.rows && data2.rows.length) {
2009
- if (pivot) {
2010
- // check if any of the pivot fields aren't in the data2.fields array
2011
- if (!isValidPivot(data2.fields)) {
2012
- const processedFields = data2.fields.map((elem) => convertPostgresColumn(elem));
2013
- setPivot(null);
2014
- setPivotData(null);
2015
- setRows(data2.rows);
2016
- setColumns(processedFields);
2017
- if (data2.rowCount) {
2018
- const processedFormData = report
2019
- ? report
2020
- : createInitialFormData(processedFields);
2021
- setNumberOfRows(data2.rowCount);
2022
- curReport = {
2023
- ...formData,
2024
- ...processedFormData,
2025
- itemQuery: data2.itemQuery,
2026
- rowCount: data2.rowCount,
2027
- filtersApplied: [],
2028
- rows: data2.rows,
2029
- columns: processedFields,
2030
- };
2031
- setTempReport(curReport);
2032
- }
2033
- const formattedRows = formatRows(data2.rows, processedFields, false);
2034
- setFormattedRows(formattedRows);
2035
- return;
2036
- }
2037
- curReport = {
2038
- ...formData,
2039
- itemQuery: data2.itemQuery,
2040
- rowCount: data2.rowCount,
2041
- filtersApplied: [],
2042
- rows: data2.rows,
2043
- };
2044
- // Do all of this to make sure we have the right unique columns when applying a pivot
2045
- let dateBucket = undefined;
2046
- const tempDateRange = dateRangesTemp &&
2047
- pivot.rowField &&
2048
- dateRangesTemp[pivot.rowField];
2049
- if (tempDateRange) {
2050
- dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
2051
- }
2052
- let distinctValuesForQuery = {};
2053
- if (pivot.columnField) {
2054
- const sqlQuery = await fetchSqlQuery({ ...baseAst, where: curFormData }, null, false);
2055
- distinctValuesForQuery = await getUniqueValuesByColumns([
2056
- {
2057
- field: pivot.columnField,
2058
- label: pivot.columnField,
2059
- format: 'string',
2060
- },
2061
- ], sqlQuery, [], client, customFields);
2062
- }
2063
- const pivotedData = await generatePivotTable(
2064
- // @ts-ignore
2065
- pivot, data2.rows, undefined, false, -1, undefined, dateBucket, curReport, client, distinctValuesForQuery ? distinctValuesForQuery : undefined);
2066
- console.info(`%c[Pivot]: ${JSON.stringify(pivot)}`, 'color: dimgray');
2067
- const processedFields = data2.fields.map((elem) => convertPostgresColumn(elem));
2068
- setPivotData(pivotedData);
2069
- setRows(data2.rows);
2070
- setColumns(processedFields);
2071
- if (data2.rowCount) {
2072
- const processedFormData = report
2073
- ? report
2074
- : createInitialFormData(processedFields);
2075
- setNumberOfRows(data2.rowCount);
2076
- setTempReport({
2077
- ...formData,
2078
- ...processedFormData,
2079
- itemQuery: data2.itemQuery,
2080
- rowCount: data2.rowCount,
2081
- filtersApplied: [],
2082
- rows: data2.rows,
2083
- columns: processedFields,
2084
- });
2085
- }
2086
- const formattedRows = formatRows(pivotedData.rows, processedFields, true, pivot.aggregationType);
2087
- setSelectedColumns(processedFields.map((column) => {
2088
- return `${table}.${column.field}`;
2089
- }));
2090
- setFormattedRows(formattedRows);
2091
- }
2092
- else {
2093
- const processedFields = data2.fields.map((elem) => convertPostgresColumn(elem));
2094
- setRows(data2.rows);
2095
- setColumns(processedFields);
2096
- if (data2.rowCount) {
2097
- const processedFormData = report
2098
- ? report
2099
- : createInitialFormData(processedFields);
2100
- setNumberOfRows(data2.rowCount);
2101
- setTempReport({
2102
- ...formData,
2103
- ...processedFormData,
2104
- itemQuery: data2.itemQuery,
2105
- rowCount: data2.rowCount,
2106
- filtersApplied: [],
2107
- rows: data2.rows,
2108
- columns: processedFields,
2109
- });
2110
- }
2111
- setSelectedColumns(processedFields.map((column) => {
2112
- return `${table}.${column.field}`;
2113
- }));
2114
- const formattedRows = formatRows(data2.rows, processedFields, false);
2115
- setFormattedRows(formattedRows);
2116
- if (data2.errorMessage) {
2117
- setErrorMessage(`Error: ${data2.errorMessage}`);
2118
- }
2119
- }
2120
- }
2121
- else {
2122
- setRows([]);
2123
- setColumns([]);
2124
- setFormattedRows([]);
2125
- setPivotData(null);
2126
- }
2127
- setLoading(false);
2128
- setDataDisplayed(true);
2129
- return rows;
2130
- }
2131
- catch (e) {
2132
- setErrorMessage(e.message);
2133
- setLoading(false);
2134
- setDataDisplayed(true);
2135
- setRows([]);
2136
- setColumns([]);
2137
- setFormattedRows([]);
2138
- setPivotData(null);
2139
- return { error: true, message: e.message };
2140
- }
2141
- }
2142
- };
2143
- // Returns whether a where-clause contains a nested subquery.
2144
- const isSubquery = (node) => {
2145
- if (!node)
2146
- return false;
2147
- if (node.ast)
2148
- return true;
2149
- if (node.left && isSubquery(node.left))
2150
- return true;
2151
- if (node.right && isSubquery(node.right))
2152
- return true;
2153
- if (node.value && Array.isArray(node.value)) {
2154
- for (const value of node.value) {
2155
- if (isSubquery(value))
2156
- return true;
2157
- }
2158
- }
2159
- return false;
2160
- };
2161
- const handleAsk = async (overridePrompt = '') => {
2162
- if (!aiPrompt && !overridePrompt) {
2163
- return;
2164
- }
2165
- try {
2166
- setLoading(true);
2167
- setAskedAQuestion(true);
2168
- setErrorMessage('');
2169
- let res, data, ast;
2170
- let numRetries = 0;
2171
- const MAX_RETRIES = 3;
2172
- // refetch the request if it comes back and we know it's invalid.
2173
- // TODO: remove this to allow joins later down the road
2174
- let isTableJoin = !ast || !ast.from || ast.from.length !== 1;
2175
- while (isTableJoin || isSubquery(ast?.where)) {
2176
- if (numRetries === MAX_RETRIES)
2177
- break;
2178
- if (!activeQuery || (ast && (isTableJoin || isSubquery(ast?.where)))) {
2179
- res = await fetch(`${QUILL_SERVER}/magic`, {
2180
- method: 'POST',
2181
- headers: { 'Content-Type': 'application/json' },
2182
- body: JSON.stringify({
2183
- initialQuestion: aiPrompt || overridePrompt,
2184
- publicKey: client.publicKey,
2185
- useNewNodeSql: true, // new flag
2186
- }),
2187
- });
2188
- }
2189
- else {
2190
- res = await fetch(`${QUILL_SERVER}/magic/edit`, {
2191
- method: 'POST',
2192
- headers: { 'Content-Type': 'application/json' },
2193
- body: JSON.stringify({
2194
- sqlQuery: activeQuery,
2195
- initialQuestion: aiPrompt,
2196
- publicKey: client.publicKey,
2197
- useNewNodeSql: true, // new flag
2198
- }),
2199
- });
2200
- }
2201
- data = await res.json();
2202
- ast = data?.ast?.length ? data?.ast[0] : data?.ast;
2203
- // TODO: Debug invalid table joins in handleAsk
2204
- isTableJoin =
2205
- ast?.type !== 'bigquery' &&
2206
- (!ast || !ast.from || ast.from.length !== 1);
2207
- numRetries += 1;
2208
- }
2209
- if (numRetries === MAX_RETRIES) {
2210
- console.error('[Error]: Max retries exceeded.');
2211
- console.info(`%c[Prompt]: ${aiPrompt}`, 'color: dimgray');
2212
- setErrorMessage("Error: Couldn't process your request, please re-word your prompt.");
2213
- return;
2214
- }
2215
- let currentSchema = schema;
2216
- if (currentSchema && currentSchema.length === 0) {
2217
- currentSchema = await fetchSchema();
2218
- }
2219
- let newAst, groupByPivot;
2220
- if (ast) {
2221
- // Unwrap the ast object, supporting many possible types
2222
- ast = ast.length ? ast[0] : ast;
2223
- newAst = convertBigQuery(ast);
2224
- newAst = convertWildcardColumns(newAst, currentSchema); // must go before groupby
2225
- ({ ast: newAst, pivot: groupByPivot } = convertGroupBy(newAst, pivot, currentSchema));
2226
- newAst = convertStringComparison(newAst, client.databaseType);
2227
- newAst = convertRemoveSimpleParentheses(newAst);
2228
- const table = getTableNames(newAst)[0] ?? initialTableName;
2229
- const tableAlias = getTableAliases(newAst)[0] ?? initialTableName;
2230
- newAst = convertUnaryToBinary(newAst);
2231
- newAst = removeNonSelectedTableReferences(newAst, tableAlias ?? table, getAllPossibleColumns().map((col) => col.name));
2232
- const procesedColumns = deepCopy(newAst).columns?.map((column) => {
2233
- if (column.expr.type === 'column_ref') {
2234
- const columnName = extractColumnish(column.expr);
2235
- return `${table}.${columnName}`;
2236
- }
2237
- else if (column.as) {
2238
- return `${table}.${column.as}`;
2239
- }
2240
- return `${table}.${column.expr.value}`;
2241
- });
2242
- setSelectedColumns(procesedColumns);
2243
- if (groupByPivot) {
2244
- setBaseAst(deepCopy({ ...newAst, orderby: null, limit: null }));
2245
- newAst = deepCopy({ ...newAst, orderby: null, limit: null });
2246
- }
2247
- else {
2248
- setBaseAst(deepCopy({ ...newAst }));
2249
- }
2250
- setFormData(deepCopy(newAst.where));
2251
- setTopLevelBinaryOperator(
2252
- // @ts-ignore
2253
- newAst?.where ? newAst?.where?.operator : 'AND');
2254
- }
2255
- ast = newAst; // so we fetch data for newAst later.
2256
- fetchSqlQuery(ast, undefined, false);
2257
- const table = getTableNames(newAst)[0] ?? initialTableName;
2258
- const hostedBody = {
2259
- metadata: {
2260
- clientId: client.publicKey,
2261
- ast,
2262
- publicKey: client.publicKey,
2263
- orgId: client.customerId,
2264
- task: 'patterns',
2265
- additionalProcessing: { page: { currentPage: 0, rowsPerPage: 20 } },
2266
- useUpdatedDataGathering: true,
2267
- pivot: groupByPivot,
2268
- useNewNodeSql: true, // new flag
2269
- },
2270
- };
2271
- let currentUniqueValues = uniqueValues;
2272
- let dateRangesTemp = dateRanges;
2273
- if ((currentUniqueValues &&
2274
- currentUniqueValues[table] &&
2275
- Object.keys(currentUniqueValues[table]).length === 0) ||
2276
- table !== currentTable) {
2277
- const tableInfo = currentSchema.find((tableInfo) => tableInfo.name === table);
2278
- if (tableInfo) {
2279
- const newUniqueValues = await getUniqueStringValues(tableInfo.columns, table);
2280
- currentUniqueValues = newUniqueValues;
2281
- if (hashCode(uniqueValues) !== hashCode(newUniqueValues)) {
2282
- setUniqueValues(newUniqueValues);
2283
- }
2284
- dateRangesTemp = await getDateRanges(tableInfo.columns, table);
2285
- setDateRanges(dateRangesTemp);
2286
- }
2287
- setCurrentTable(table);
2288
- }
2289
- const cloudBody = {};
2290
- const data2 = await getData(client, 'patterns', 'same-origin', hostedBody, cloudBody);
2291
- if (!data2 || data2.status === 'error') {
2292
- throw new Error('Error querying data from patterns');
2293
- }
2294
- if (data2.rows && data2.rows.length) {
2295
- const processedFields = data2.fields
2296
- .map((elem) => convertPostgresColumn(elem))
2297
- .map((elem) => {
2298
- const tableInfo = currentSchema.find((t) => t.name === table);
2299
- const columnInfo = tableInfo?.columns.find((column) => column.name === elem.field);
2300
- return columnInfo
2301
- ? convertColumnInfoToColumnInternal(columnInfo)
2302
- : null;
2303
- })
2304
- .filter((elem) => elem);
2305
- let possiblePivot = true;
2306
- const possibleColumns = getPossiblePivotFieldOptions(processedFields, currentUniqueValues[table]);
2307
- if (groupByPivot &&
2308
- ((groupByPivot.columnField &&
2309
- !possibleColumns.columnFields.includes(groupByPivot?.columnField)) ||
2310
- (groupByPivot.rowField &&
2311
- !possibleColumns.rowFields.includes(groupByPivot?.rowField)) ||
2312
- (groupByPivot.valueField &&
2313
- !possibleColumns.valueFields.includes(groupByPivot?.valueField || '')))) {
2314
- possiblePivot = false;
2315
- let errorMessageEnding = '';
2316
- if (groupByPivot.columnField &&
2317
- !possibleColumns.columnFields.includes(groupByPivot?.columnField || '')) {
2318
- if (currentUniqueValues[table]?.[groupByPivot?.columnField || '']) {
2319
- errorMessageEnding = `The column ${groupByPivot?.columnField} has more than 24 unique values to pivot on.`;
2320
- }
2321
- else {
2322
- errorMessageEnding = `The column ${groupByPivot?.columnField} is not a proper column field.`;
2323
- }
2324
- }
2325
- else if (groupByPivot.rowField &&
2326
- !possibleColumns.rowFields.includes(groupByPivot?.rowField || '')) {
2327
- if (currentUniqueValues[table]?.[groupByPivot?.rowField || '']) {
2328
- errorMessageEnding = `The column ${groupByPivot?.rowField} has more than 36 unique values to pivot on.`;
2329
- }
2330
- else {
2331
- errorMessageEnding = `The column ${groupByPivot?.rowField} is not a proper row field.`;
2332
- }
2333
- }
2334
- else if (groupByPivot.valueField &&
2335
- !possibleColumns.valueFields.includes(groupByPivot?.valueField || '')) {
2336
- errorMessageEnding = `The column ${groupByPivot?.valueField} is not a proper value field.`;
2337
- }
2338
- setErrorMessage(`The requested pivot is not supported. ${errorMessageEnding}`);
2339
- }
2340
- if (groupByPivot && possiblePivot) {
2341
- let curReport = report ? report : undefined;
2342
- if (data2.rowCount) {
2343
- const processedFormData = report
2344
- ? report
2345
- : createInitialFormData(processedFields);
2346
- setNumberOfRows(data2.rowCount);
2347
- curReport = {
2348
- ...formData,
2349
- ...processedFormData,
2350
- itemQuery: data2.itemQuery,
2351
- rowCount: data2.rowCount,
2352
- filtersApplied: [],
2353
- rows: data2.rows,
2354
- columns: processedFields,
2355
- };
2356
- setTempReport(curReport || null);
2357
- }
2358
- let dateBucket = undefined;
2359
- const tempDateRange = dateRangesTemp &&
2360
- groupByPivot.rowField &&
2361
- dateRangesTemp[groupByPivot.rowField];
2362
- if (tempDateRange) {
2363
- dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
2364
- }
2365
- const pivotedData = await generatePivotTable(
2366
- // @ts-ignore
2367
- groupByPivot, data2.rows, undefined, false, -1, undefined, dateBucket, curReport, client, groupByPivot.columnField
2368
- ? currentUniqueValues[groupByPivot.columnField]
2369
- : undefined);
2370
- console.info(`%c[Pivot]: ${JSON.stringify(groupByPivot)}`, 'color: dimgray');
2371
- setPivotData(pivotedData);
2372
- setPivot(groupByPivot);
2373
- if (client.databaseType &&
2374
- client.databaseType.toLowerCase() === 'bigquery') {
2375
- parseValueFromBigQueryDates(data2.rows, processedFields);
2376
- }
2377
- setRows(data2.rows);
2378
- setPivotRowField(groupByPivot?.rowField);
2379
- setPivotColumnField(groupByPivot?.columnField);
2380
- setPivotValueField(groupByPivot?.valueField);
2381
- setPivotAggregation(groupByPivot?.aggregationType);
2382
- setColumns(processedFields);
2383
- const formattedRows = formatRows(pivotedData.rows, processedFields, true, groupByPivot.aggregationType);
2384
- setFormattedRows(formattedRows);
2385
- }
2386
- else {
2387
- const processedFields = data2.fields.map((elem) => convertPostgresColumn(elem));
2388
- if (client.databaseType &&
2389
- client.databaseType.toLowerCase() === 'bigquery') {
2390
- parseValueFromBigQueryDates(data2.rows, processedFields);
2391
- }
2392
- setRows(data2.rows);
2393
- setColumns(processedFields);
2394
- if (data2.rowCount) {
2395
- setNumberOfRows(data2.rowCount);
2396
- setTempReport({
2397
- ...formData,
2398
- itemQuery: data2.itemQuery,
2399
- rowCount: data2.rowCount,
2400
- filtersApplied: [],
2401
- rows: data2.rows,
2402
- columns: processedFields,
2403
- });
2404
- }
2405
- const formattedRows = formatRows(data2.rows, processedFields, false);
2406
- setFormattedRows(formattedRows);
2407
- }
2408
- return data2.rows;
2409
- }
2410
- else {
2411
- setPivotData([]);
2412
- setRows([]);
2413
- setColumns([]);
2414
- setFormattedRows([]);
2415
- }
2416
- if (data2.query) {
2417
- setActiveQuery(data2.query);
2418
- }
2419
- else {
2420
- setActiveQuery('');
2421
- }
2422
- if (data2.errorMessage) {
2423
- setErrorMessage(`Error: Couldn't process your request, please re-word your prompt.`);
2424
- }
2425
- }
2426
- catch (e) {
2427
- console.error(e);
2428
- setErrorMessage(`Error: Couldn't process your request, please re-word your prompt.`);
2429
- }
2430
- finally {
2431
- setLoading(false);
2432
- setDataDisplayed(true);
2433
- }
2434
- };
2435
- const handleDeleteColumn = (name) => {
2436
- if (!baseAst || !baseAst.columns.length || selectedColumns.length === 1) {
2437
- clearAllState();
2438
- return;
2439
- }
2440
- setSelectedColumns((selectedColumns) => selectedColumns.filter((column) => !column.endsWith(name)));
2441
- const columns = baseAst.columns.filter((col) => {
2442
- if (col.expr.type === 'column_ref') {
2443
- return (col.expr.column !== name &&
2444
- (!col.expr.column.expr || col.expr.column.expr.value !== name));
2445
- }
2446
- else if (col.as) {
2447
- return col.as !== name;
2448
- }
2449
- return col.expr.value !== name;
2450
- });
2451
- if (columns.length === 0) {
2452
- clearAllState();
2453
- return;
2454
- }
2455
- const newAst = deepCopy({ ...baseAst, columns });
2456
- setBaseAst(newAst);
2457
- fetchSqlQuery(newAst);
2458
- };
2459
- const DraggableItem = ({ id, label, onDelete }) => {
2460
- const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: id });
2461
- const style = {
2462
- transform: DND_CSS.Transform.toString(transform),
2463
- transition,
2464
- };
2465
- return (_jsx("div", { style: { ...style }, ref: setNodeRef, children: _jsx(DraggableColumnComponent, { label: snakeAndCamelCaseToTitleCase(label), onDelete: onDelete, DragHandle: (props) => (_jsx("div", { style: {
2466
- cursor: 'grab',
2467
- }, ...attributes, ...listeners, children: _jsx(props.dragIcon, {}) })) }) }));
2468
- };
2469
- function DraggableColumns() {
2470
- const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, {
2471
- coordinateGetter: sortableKeyboardCoordinates,
2472
- }));
2473
- // When a drag event ends, switch the item order.
2474
- function handleDragEnd(event) {
2475
- const { active, over } = event;
2476
- if (!active || !over)
2477
- return;
2478
- if (active.id !== over.id) {
2479
- const oldIndex = orderedColumnNames.findIndex((c) => c.endsWith(`${currentTable}.${active.id}`));
2480
- const newIndex = orderedColumnNames.findIndex((c) => c.endsWith(`${currentTable}.${over.id}`));
2481
- const newOrder = arrayMove(orderedColumnNames, oldIndex, newIndex);
2482
- setOrderedColumnNames(newOrder);
2483
- const orderedSelectedColumns = [];
2484
- for (const value of newOrder) {
2485
- const column = value.split('.')[1];
2486
- if (selectedColumns.includes(value)) {
2487
- orderedSelectedColumns.push(column);
2488
- }
2489
- }
2490
- setSelectedOrderedColumns(orderedSelectedColumns);
2491
- // If there is already an AST saved in state, only update the columns
2492
- // otherwise fill in the defaultAST shape and also update columns.
2493
- const fallbackAST = {
2494
- ...defaultAST,
2495
- from: [{ ...defaultTable }],
2496
- columns: orderedSelectedColumns.map((name) => nameToColumn(name)),
2497
- };
2498
- const newBaseAst = {
2499
- ...baseAst,
2500
- columns: baseAst?.columns.length
2501
- ? orderedSelectedColumns.map((name) => nameToColumn(name))
2502
- : baseAst?.columns,
2503
- };
2504
- const newAst = baseAst ? newBaseAst : fallbackAST;
2505
- setBaseAst(newAst);
2506
- fetchSqlQuery(newAst, undefined, false);
2507
- }
2508
- }
2509
- const columnNamesInAst = baseAst?.columns
2510
- .map((col) => {
2511
- if (col.expr.type === 'column_ref' && col.expr.column) {
2512
- if (typeof col.expr.column === 'string') {
2513
- return col.expr.column;
2514
- }
2515
- else {
2516
- return col.expr.column.expr.value;
2517
- }
2518
- }
2519
- else if (col.as) {
2520
- if (typeof col.as === 'string') {
2521
- return col.as;
2522
- }
2523
- else {
2524
- return col.as.expr?.value;
2525
- }
2526
- }
2527
- else if (col.expr && col.expr.type === 'aggr_func') {
2528
- if (col.expr.args) {
2529
- return `${col.expr.name.toLowerCase()}(${col.expr.args.expr.value})`;
2530
- }
2531
- return col.expr.name;
2532
- }
2533
- return col.expr.value;
2534
- })
2535
- .filter(
2536
- // remove duplicate entries
2537
- (value, index, self) => value && self.indexOf(value) === index) ?? [];
2538
- return (_jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: columnNamesInAst, strategy: verticalListSortingStrategy, children: _jsxs("div", { style: {
2539
- display: 'flex',
2540
- flexDirection: 'column',
2541
- gap: 8,
2542
- }, children: [columnNamesInAst.map((name) => (_jsx(DraggableItem, { id: name, label: name, onDelete: () => handleDeleteColumn(name) }, name))), columnNamesInAst?.length > 0 && _jsx("div", { style: { height: 6 } })] }) }) }));
2543
- }
2544
- if (loading || initialChartLoad) {
2545
- return (_jsxs("div", { style: {
2546
- display: 'flex',
2547
- flexDirection: 'row',
2548
- height: '100%',
2549
- ...containerStyle,
2550
- }, className: className, ref: parentRef, children: [_jsxs(SidebarComponent, { children: [_jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Columns" }), _jsx(DraggableColumns, {}), _jsx(SecondaryButtonComponent, { onClick: () => {
2551
- if (!openPopover) {
2552
- setOpenPopover('AddColumnModal');
2553
- }
2554
- }, label: 'Select columns' }), _jsx(ModalComponent, { isOpen: openPopover === 'AddColumnModal', setIsOpen: (isOpen) => {
2555
- if (!isOpen) {
2556
- // delay onClose callback so onClick no-ops
2557
- setTimeout(() => {
2558
- setIsPending(false);
2559
- setActiveEditItem(null);
2560
- setActivePath(null);
2561
- setOpenPopover(null);
2562
- }, 100);
2563
- }
2564
- }, title: "Select columns", children: _jsx(AddColumnModal, { onSave: () => {
2565
- setActiveEditItem(null);
2566
- setActivePath(null);
2567
- setOpenPopover(null);
2568
- }, orderedColumnNames: orderedColumnNames, setOrderedColumnNames: setOrderedColumnNames, selectedColumns: selectedColumns, setSelectedColumns: setSelectedColumns, isSelectedAllColumns: isSelectedAllColumns, clearAllState: clearAllState, nameToColumn: nameToColumn, baseAst: baseAst, setBaseAst: (ast) => {
2569
- setBaseAst(ast);
2570
- fetchSqlQuery(ast);
2571
- }, pivot: pivot, initialTableName: initialTableName, defaultAST: defaultAST, defaultTable: defaultTable, setPivot: setPivot, TextInput: TextInputComponent, SelectColumn: SelectColumnComponent, SecondaryButton: SecondaryButtonComponent, Button: ButtonComponent, ColumnSearchEmptyState: ColumnSearchEmptyState, LoadingComponent: LoadingComponent }) })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Filters" }), formData && (_jsx("div", { style: {
2572
- display: 'flex',
2573
- flexDirection: 'column',
2574
- gap: 8,
2575
- marginBottom: 12,
2576
- }, children: filterTree && renderFilters(filterTree) })), _jsxs("div", { style: {
2577
- display: 'flex',
2578
- flexDirection: 'column',
2579
- alignItems: 'flex-start',
2580
- }, children: [_jsx(SecondaryButtonComponent, { onClick: () => {
2581
- if (!selectedColumns ||
2582
- selectedColumns.length === 0 ||
2583
- loading) {
2584
- return;
2585
- }
2586
- if (!openPopover) {
2587
- const value = orderedColumnNames[0];
2588
- const column = value.split('.')[1];
2589
- const columnType = getColumnTypeByName(column);
2590
- if (isNumericColumnType(columnType)) {
2591
- const newSubtree = deepCopy(defaultNumericComparison);
2592
- newSubtree.left.column = column;
2593
- setActiveEditItem(newSubtree);
2594
- }
2595
- else {
2596
- const newSubtree = deepCopy(defaultEntry);
2597
- newSubtree.left.args.value[0].column = column;
2598
- setActiveEditItem(newSubtree);
2599
- }
2600
- setOpenPopover('AddFilterPopover');
2601
- setActivePath('');
2602
- setIsPending(true);
2603
- }
2604
- }, label: 'Add filter' }), _jsx("div", { style: {
2605
- position: 'relative',
2606
- ...(openPopover === 'AddFilterPopover' && { top: 12 }),
2607
- }, children: _jsx(PopoverComponent, { isOpen: openPopover === 'AddFilterPopover', setIsOpen: (isOpen) => {
2608
- if (!isOpen) {
2609
- setIsPending(false);
2610
- setActivePath(null);
2611
- setOpenPopover(null);
2612
- setTimeout(() => {
2613
- clearCheckboxes();
2614
- setActiveEditItem(null);
2615
- }, 300);
2616
- }
2617
- }, popoverTitle: 'Add filter', popoverChildren: _jsx(FilterModal, { schema: schema.find((s) => s.name === currentTable ||
2618
- s.displayName === currentTable) ?? schema[0], fieldValuesMap: uniqueValues[getTableNames(baseAst).length === 1
2619
- ? getTableNames(baseAst)[0]
2620
- : initialTableName], onSubmitFilter: (filter) => {
2621
- setOpenPopover(null);
2622
- setIsPending(false);
2623
- const item = filterToAst(filter, client.databaseType.toLowerCase());
2624
- handleInsertion(item, 'AND', false);
2625
- }, onDeleteFilter: () => { }, ButtonComponent: ButtonComponent, SelectComponent: SelectComponent, TextInputComponent: TextInputComponent, SecondaryButtonComponent: SecondaryButtonComponent, MultiSelectComponent: MultiSelectComponent }) }) }), baseAst?.where &&
2626
- false && ( // temp removed the AddConditionPopover
2627
- _jsxs(_Fragment, { children: [_jsx(SecondaryButtonComponent, { onClick: () => {
2628
- if (!openPopover) {
2629
- setActiveEditItem(deepCopy(defaultEntry));
2630
- setOpenPopover('AddConditionPopover');
2631
- setActivePath('');
2632
- setIsPending(true);
2633
- }
2634
- }, label: "Add condition" }), _jsx(PopoverComponent, { isOpen: openPopover === 'AddConditionPopover', setIsOpen: (isOpen) => {
2635
- if (!isOpen) {
2636
- setIsPending(false);
2637
- setTimeout(() => {
2638
- clearCheckboxes();
2639
- setActiveEditItem(null);
2640
- }, 300);
2641
- setActivePath(null);
2642
- setOpenPopover(null);
2643
- }
2644
- }, popoverTitle: "Add condition", popoverChildren: _jsx(AddConditionPopover, { onSave: () => {
2645
- if (isNodeEmptyCollection(activeEditItem)) {
2646
- setIsPending(false);
2647
- setTimeout(() => {
2648
- setActiveEditItem(null);
2649
- clearCheckboxes();
2650
- }, 300);
2651
- setActivePath(null);
2652
- setOpenPopover(null);
2653
- }
2654
- else {
2655
- setIsPending(false);
2656
- handleInsertion(activeEditItem, topLevelBinaryOperator, true);
2657
- setTimeout(() => {
2658
- setActiveEditItem(null);
2659
- clearCheckboxes();
2660
- }, 300);
2661
- setActivePath(null);
2662
- setOpenPopover(null);
2663
- }
2664
- } }) })] }))] })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Pivot" }), _jsx(PivotModal, { pivotRowField: pivotRowField, setPivotRowField: setPivotRowField, pivotColumnField: pivotColumnField, setPivotColumnField: setPivotColumnField, pivotValueField: pivotValueField, setPivotValueField: setPivotValueField, pivotAggregation: pivotAggregation, setPivotAggregation: setPivotAggregation, createdPivots: createdPivots, setCreatedPivots: setCreatedPivots, recommendedPivots: recommendedPivots, setRecommendedPivots: setRecommendedPivots, popUpTitle: pivotPopUpTitle, setPopUpTitle: setPivotPopUpTitle, selectedTable: initialTableName, CardComponent: CardComponent, SelectComponent: SelectComponent, ButtonComponent: ButtonComponent, PopoverComponent: PopoverComponent, TextComponent: TextComponent, ErrorMessageComponent: ErrorMessageComponent, PivotRowContainer: PivotRowContainer, PivotColumnContainer: PivotColumnContainer, LoadingComponent: LoadingComponent, isOpen: showPivotPopover, setIsOpen: setShowPivotPopover, showUpdatePivot: isEditingPivot, setShowUpdatePivot: setIsEditingPivot, parentRef: parentRef, data: rows, columns: columns, triggerButtonText: 'Add pivot', selectedPivotIndex: selectedPivotIndex, setSelectedPivotIndex: setSelectedPivotIndex, removePivot: () => {
2665
- setPivot(null);
2666
- setPivotData(null);
2667
- const formattedRows = formatRows(rows, columns, false);
2668
- setFormattedRows(formattedRows);
2669
- },
2670
- // TODOs
2671
- selectPivot: () => {
2672
- return;
2673
- }, selectPivotOnEdit: true, showTrigger: !pivot, theme: theme, LabelComponent: LabelComponent, HeaderComponent: HeaderComponent, dateRange: undefined, pivotCountRequest: 4, SecondaryButtonComponent: SecondaryButtonComponent, query: activeQuery, initialUniqueValues: uniqueValues[currentTable], disabled: !loading && (!baseAst || !dataDisplayed), pivotRecommendationsEnabled: pivotRecommendationsEnabled && overrideRecommendations, report: tempReport ?? report }), pivot && (_jsx(PivotForm, { columns: columns, uniqueValues: uniqueValues[currentTable], setPivotRowField: (value) => {
2674
- setPivotRowField(value);
2675
- }, setPivotColumnField: setPivotColumnField, setPivotValueField: setPivotValueField, setPivotAggregation: setPivotAggregation, pivotRowField: pivotRowField, pivotColumnField: pivotColumnField, pivotValueField: pivotValueField, pivotAggregation: pivotAggregation, onDelete: () => {
2676
- setPivot(null);
2677
- setPivotData([]);
2678
- const formattedRows = formatRows(rows, columns, false);
2679
- setFormattedRows(formattedRows);
2680
- }, SecondaryButtonComponent: SecondaryButtonComponent, SelectComponent: SelectComponent, PivotColumnContainer: PivotColumnContainer }))] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Sort" }), pivot && pivot.sort && (_jsx("div", { style: {
2681
- display: 'flex',
2682
- flexDirection: 'column',
2683
- gap: 8,
2684
- marginBottom: 12,
2685
- }, children: _jsx(SortSentence, { sortData: {
2686
- type: pivot.sortDirection,
2687
- expr: { type: 'column_ref', column: pivot.sortField },
2688
- }, columns: selectedColumns, setIsPending: setIsPending, setEditPopoverKey: () => { }, setActiveEditItem: setActiveEditItem, setActivePath: setActivePath, setOpenPopover: setOpenPopover, SortPopover: SortPopoverComponent, EditPopover: AddSortPopover, handleDelete: () => {
2689
- setPivot({ ...pivot, sort: false });
2690
- setBaseAst(deepCopy(baseAst));
2691
- if (!pivot) {
2692
- fetchSqlQuery(baseAst);
2693
- }
2694
- }, onSave: (column, direction) => {
2695
- const sortFieldType = column === (pivot.valueField || 'count')
2696
- ? 'number'
2697
- : pivot.rowFieldType;
2698
- setPivot({
2699
- ...pivot,
2700
- sort: true,
2701
- sortDirection: direction,
2702
- sortField: column,
2703
- sortFieldType: sortFieldType,
2704
- });
2705
- setOpenPopover(null);
2706
- setBaseAst(deepCopy(baseAst));
2707
- if (!pivot) {
2708
- fetchSqlQuery(baseAst);
2709
- }
2710
- }, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent }, `sort-sentence-pivot`) })), baseAst && baseAst.orderby && (_jsx("div", { style: {
2711
- display: 'flex',
2712
- flexDirection: 'column',
2713
- gap: 8,
2714
- marginBottom: 12,
2715
- }, children: baseAst.orderby.map((sortData, id) => (_jsx(SortSentence, { sortData: sortData, columns: selectedColumns, onSave: (column, direction) => {
2716
- setIsPending(false);
2717
- setActiveEditItem(null);
2718
- setOpenPopover(null);
2719
- if (column === '')
2720
- return;
2721
- const newAst = { ...baseAst };
2722
- newAst.orderby[id] = {
2723
- expr: {
2724
- type: 'column_ref',
2725
- table: null,
2726
- column: column,
2727
- },
2728
- type: direction,
2729
- };
2730
- // look through the columns
2731
- setActivePath(null);
2732
- setOpenPopover(null);
2733
- setBaseAst(deepCopy(newAst));
2734
- if (!pivot) {
2735
- fetchSqlQuery(newAst);
2736
- }
2737
- }, setIsPending: setIsPending, setEditPopoverKey: () => { }, setActiveEditItem: setActiveEditItem, setActivePath: setActivePath, setOpenPopover: setOpenPopover, SortPopover: SortPopoverComponent, EditPopover: AddSortPopover, handleDelete: () => {
2738
- const newAst = { ...baseAst };
2739
- newAst.orderby.splice(id, 1);
2740
- setBaseAst(deepCopy(newAst));
2741
- if (!pivot) {
2742
- fetchSqlQuery(newAst);
2743
- }
2744
- }, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent }, `sort-sentence-${id}`))) })), _jsx(SecondaryButtonComponent, { onClick: () => {
2745
- if (!selectedColumns ||
2746
- selectedColumns.length === 0 ||
2747
- loading) {
2748
- return;
2749
- }
2750
- if (!openPopover) {
2751
- setOpenPopover('AddSortPopover');
2752
- }
2753
- }, label: "Add sort" }), _jsx(PopoverComponent, { isOpen: openPopover === 'AddSortPopover', setIsOpen: (isOpen) => {
2754
- if (!isOpen) {
2755
- setIsPending(false);
2756
- setActiveEditItem(null);
2757
- setActivePath(null);
2758
- setOpenPopover(null);
2759
- }
2760
- }, popoverTitle: "Sort by", popoverChildren: _jsx(AddSortPopover, { columns: selectedColumns, Select: SelectComponent, Button: ButtonComponent, onSave: () => { } }) })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Limit" }), baseAst && baseAst.limit && baseAst.limit.value?.length > 0 ? (_jsx("div", { style: {
2761
- display: 'flex',
2762
- flexDirection: 'column',
2763
- gap: 8,
2764
- marginBottom: 12,
2765
- }, children: _jsx(LimitSentence, { limit: baseAst.limit, setOpenPopover: setOpenPopover, LimitPopover: LimitPopoverComponent, EditPopover: AddLimitPopover, handleDelete: () => {
2766
- const newAst = { ...baseAst };
2767
- newAst.limit = null;
2768
- setBaseAst(deepCopy(newAst));
2769
- fetchSqlQuery(newAst);
2770
- }, onSave: (limit) => {
2771
- const newAst = { ...baseAst };
2772
- newAst.limit = {
2773
- seperator: '',
2774
- value: [
2775
- {
2776
- type: 'number',
2777
- value: limit,
2778
- },
2779
- ],
2780
- };
2781
- setOpenPopover(null);
2782
- setBaseAst(deepCopy(newAst));
2783
- fetchSqlQuery(newAst);
2784
- }, TextInput: TextInputComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent }) })) : (_jsxs(_Fragment, { children: [_jsx(SecondaryButtonComponent, { onClick: () => {
2785
- if (!selectedColumns ||
2786
- selectedColumns.length === 0 ||
2787
- loading) {
2788
- return;
2789
- }
2790
- if (!openPopover) {
2791
- setOpenPopover('AddLimitPopover');
2792
- }
2793
- }, label: 'Add limit' }), _jsx(PopoverComponent, { isOpen: openPopover === 'AddLimitPopover', setIsOpen: (isOpen) => {
2794
- if (!isOpen) {
2795
- setIsPending(false);
2796
- setActiveEditItem(null);
2797
- setActivePath(null);
2798
- setOpenPopover(null);
2799
- }
2800
- }, popoverTitle: "Add limit", popoverChildren: _jsx(AddLimitPopover, { TextInput: TextInputComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, onSave: () => { } }) })] }))] })] }), _jsxs(ContainerComponent, { children: [isAIEnabled && (_jsx("form", { ref: askAILoadingContainerRef, onSubmit: (event) => {
2801
- event.preventDefault();
2802
- }, style: {
2803
- display: 'flex',
2804
- flexDirection: 'row',
2805
- gap: 12,
2806
- visibility: askAIInputWidth === -1 && askAILoadingContainerWidth === -1
2807
- ? 'hidden'
2808
- : 'visible',
2809
- }, children: _jsxs(_Fragment, { children: [_jsx(TextInputComponent, { id: "ask_ai_loading_bar", placeholder: askedAQuestion
2810
- ? 'Ask a follow-up question...'
2811
- : 'Ask a question...', width: askAIInputWidth !== -1
2812
- ? askAIInputWidth
2813
- : askAILoadingContainerWidth, value: aiPrompt, onChange: () => { } }), _jsx(ButtonComponent, { onClick: () => { }, label: "Ask AI" }), ((baseAst && dataDisplayed) ||
2814
- initialLoad ||
2815
- initialChartLoad) && (_jsx(SecondaryButtonComponent, { onClick: () => { }, label: "New report" }))] }) })), _jsxs(_Fragment, { children: [_jsx(TableComponent, { isLoading: true, rows: [], columns: [] }), baseAst && dataDisplayed && !initialChartLoad && (_jsxs("div", { style: {
2816
- display: 'flex',
2817
- flexDirection: 'row',
2818
- gap: '12px',
2819
- }, children: [_jsx("div", { style: { width: '100%' } }), !hideCopySQL && (_jsx(SecondaryButtonComponent, { onClick: () => copySQLToClipboard(), label: isCopying ? '✅ Copied' : 'Copy SQL' })), _jsx(ButtonComponent, { label: report ? 'Save changes' : 'Add to dashboard', onClick: () => { } })] }))] })] }), _jsx("style", { children: `body{margin:0;}` })] }));
2820
- }
2821
- return (_jsxs("div", { style: { backgroundColor: theme.backgroundColor, ...containerStyle }, className: className, children: [(!isChartBuilderHorizontalView ||
2822
- (isChartBuilderHorizontalView && !isChartBuilderOpen)) && (_jsxs("div", { ref: parentRef, style: {
2823
- display: 'flex',
2824
- flexDirection: 'row',
2825
- height: '100%',
2826
- overflowY: 'auto',
2827
- boxSizing: 'border-box',
2828
- ...containerStyle,
2829
- }, className: className, children: [_jsxs(SidebarComponent, { children: [_jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Columns" }), _jsx(DraggableColumns, {}), _jsx(SecondaryButtonComponent, { onClick: () => {
2830
- if (!orderedColumnNames) {
2831
- return;
2832
- }
2833
- if (!openPopover) {
2834
- setOpenPopover('AddColumnModal');
2835
- }
2836
- }, label: "Select columns" }), _jsx(ModalComponent, { isOpen: openPopover === 'AddColumnModal', setIsOpen: (isOpen) => {
2837
- if (!isOpen) {
2838
- // delay onClose callback so onClick no-ops
2839
- setTimeout(() => {
2840
- setIsPending(false);
2841
- setActiveEditItem(null);
2842
- setActivePath(null);
2843
- setOpenPopover(null);
2844
- }, 100);
2845
- }
2846
- }, title: "Select columns", children: _jsx(AddColumnModal, { onSave: () => {
2847
- setActiveEditItem(null);
2848
- setActivePath(null);
2849
- setOpenPopover(null);
2850
- }, orderedColumnNames: orderedColumnNames, setOrderedColumnNames: setOrderedColumnNames, selectedColumns: selectedColumns, setSelectedColumns: setSelectedColumns, isSelectedAllColumns: isSelectedAllColumns, clearAllState: clearAllState, nameToColumn: nameToColumn, baseAst: baseAst, setBaseAst: (ast) => {
2851
- setBaseAst(ast);
2852
- fetchSqlQuery(ast);
2853
- }, pivot: pivot, initialTableName: initialTableName, defaultAST: defaultAST, defaultTable: defaultTable, schemaLoading: loadingSchema, setPivot: setPivot, TextInput: TextInputComponent, SelectColumn: SelectColumnComponent, SecondaryButton: SecondaryButtonComponent, Button: ButtonComponent, ColumnSearchEmptyState: ColumnSearchEmptyState, LoadingComponent: LoadingComponent }) })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Filters" }), formData && (_jsx("div", { style: {
2854
- display: 'flex',
2855
- flexDirection: 'column',
2856
- gap: 8,
2857
- marginBottom: 12,
2858
- }, children: filterTree && renderFilters(filterTree) })), _jsxs("div", { style: {
2859
- display: 'flex',
2860
- flexDirection: 'column',
2861
- alignItems: 'flex-start',
2862
- }, children: [_jsx(SecondaryButtonComponent, { disabled: !baseAst || !dataDisplayed, onClick: () => {
2863
- if (!selectedColumns ||
2864
- selectedColumns.length === 0 ||
2865
- loading) {
2866
- return;
2867
- }
2868
- if (!openPopover) {
2869
- const value = orderedColumnNames[0];
2870
- const column = value.split('.')[1];
2871
- const columnType = getColumnTypeByName(column);
2872
- if (isNumericColumnType(columnType)) {
2873
- const newSubtree = deepCopy(defaultNumericComparison);
2874
- newSubtree.left.column = column;
2875
- setActiveEditItem(newSubtree);
2876
- }
2877
- else {
2878
- const newSubtree = deepCopy(defaultEntry);
2879
- newSubtree.left.args.value[0].column = column;
2880
- setActiveEditItem(newSubtree);
2881
- }
2882
- setOpenPopover('AddFilterPopover');
2883
- setActivePath('');
2884
- setIsPending(true);
2885
- }
2886
- }, label: 'Add filter' }), _jsx("div", { style: {
2887
- position: 'relative',
2888
- ...(openPopover === 'AddFilterPopover' && { top: 12 }),
2889
- }, children: _jsx(PopoverComponent, { isOpen: openPopover === 'AddFilterPopover', setIsOpen: (isOpen) => {
2890
- if (!isOpen) {
2891
- // delay onClose callback so onClick no-ops
2892
- setOpenPopover(null);
2893
- setTimeout(() => {
2894
- setIsPending(false);
2895
- setActivePath(null);
2896
- clearCheckboxes();
2897
- setActiveEditItem(null);
2898
- }, 300);
2899
- }
2900
- }, popoverTitle: "Add filter", popoverChildren: _jsx(FilterModal, { schema: schema.find((s) => s.name === currentTable ||
2901
- s.displayName === currentTable) ?? schema[0], fieldValuesMap: fieldValuesMap, onSubmitFilter: (filter) => {
2902
- setOpenPopover(null);
2903
- setIsPending(false);
2904
- const item = filterToAst(filter, client.databaseType.toLowerCase());
2905
- handleInsertion(item, 'AND', false);
2906
- }, onDeleteFilter: () => { }, ButtonComponent: ButtonComponent, SelectComponent: SelectComponent, TextInputComponent: TextInputComponent, MultiSelectComponent: MultiSelectComponent }) }) }), baseAst?.where &&
2907
- false && ( // temp removed the AddConditionPopover
2908
- _jsxs(_Fragment, { children: [_jsx(SecondaryButtonComponent, { onClick: () => {
2909
- if (!openPopover) {
2910
- setActiveEditItem(deepCopy(defaultEntry));
2911
- setOpenPopover('AddConditionPopover');
2912
- setActivePath('');
2913
- setIsPending(true);
2914
- }
2915
- }, label: 'Add condition' }), _jsx(PopoverComponent, { isOpen: openPopover === 'AddConditionPopover', setIsOpen: (isOpen) => {
2916
- if (!isOpen) {
2917
- // delay onClose callback so onClick no-ops
2918
- setTimeout(() => {
2919
- setIsPending(false);
2920
- setActiveEditItem(null);
2921
- setActivePath(null);
2922
- setOpenPopover(null);
2923
- clearCheckboxes();
2924
- }, 200);
2925
- }
2926
- }, popoverChildren: _jsx(AddConditionPopover, { onSave: () => {
2927
- if (isNodeEmptyCollection(activeEditItem)) {
2928
- setIsPending(false);
2929
- setTimeout(() => {
2930
- setActiveEditItem(null);
2931
- }, 300);
2932
- setActivePath(null);
2933
- setOpenPopover(null);
2934
- clearCheckboxes();
2935
- }
2936
- else {
2937
- setIsPending(false);
2938
- handleInsertion(activeEditItem, topLevelBinaryOperator, true);
2939
- setTimeout(() => {
2940
- setActiveEditItem(null);
2941
- }, 300);
2942
- setActivePath(null);
2943
- setOpenPopover(null);
2944
- clearCheckboxes();
2945
- }
2946
- } }) })] }))] })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Pivot" }), _jsx(PivotModal, { pivotRowField: pivotRowField, setPivotRowField: setPivotRowField, pivotColumnField: pivotColumnField, setPivotColumnField: setPivotColumnField, pivotValueField: pivotValueField, setPivotValueField: setPivotValueField, pivotAggregation: pivotAggregation, setPivotAggregation: setPivotAggregation, createdPivots: createdPivots, setCreatedPivots: setCreatedPivots, recommendedPivots: recommendedPivots, setRecommendedPivots: setRecommendedPivots, popUpTitle: pivotPopUpTitle, setPopUpTitle: setPivotPopUpTitle, selectedTable: initialTableName, SelectComponent: SelectComponent, ButtonComponent: ButtonComponent, CardComponent: CardComponent, SecondaryButtonComponent: SecondaryButtonComponent, PopoverComponent: PopoverComponent, TextComponent: TextComponent, ErrorMessageComponent: ErrorMessageComponent, PivotRowContainer: PivotRowContainer, PivotColumnContainer: PivotColumnContainer, LoadingComponent: LoadingComponent, isOpen: showPivotPopover, setIsOpen: setShowPivotPopover, showUpdatePivot: isEditingPivot, setShowUpdatePivot: setIsEditingPivot, parentRef: parentRef, data: rows, columns: columns, triggerButtonText: 'Add pivot', selectedPivotIndex: selectedPivotIndex, setSelectedPivotIndex: setSelectedPivotIndex, removePivot: () => {
2947
- setPivot(null);
2948
- setPivotData(null);
2949
- const formattedRows = formatRows(rows, columns, false);
2950
- setFormattedRows(formattedRows);
2951
- }, selectPivot: async (pivot, uniqueValues, dateRange, pivotTable) => {
2952
- if (!pivot)
2953
- return;
2954
- const newAst = { ...baseAst };
2955
- newAst.orderby = null;
2956
- if (pivot.rowFieldType === 'date') {
2957
- pivot['sort'] = true;
2958
- pivot['sortDirection'] = 'ASC';
2959
- }
2960
- setBaseAst(newAst); // trigger refetch
2961
- let dateBucket = undefined;
2962
- if (dateRange) {
2963
- dateBucket = getDateBucketFromRange(dateRange);
2964
- }
2965
- if (!pivotTable) {
2966
- pivotTable = await generatePivotTable(pivot, rows, undefined, false, -1, undefined, dateBucket, tempReport, client, uniqueValues);
2967
- }
2968
- setPivotData(pivotTable || []);
2969
- setPivot(pivot);
2970
- const formattedRows = formatRows(pivotTable.rows, columns, true, pivot.aggregationType);
2971
- setFormattedRows(formattedRows);
2972
- setErrorMessage('');
2973
- }, selectPivotOnEdit: true, showTrigger: !pivot, theme: theme, LabelComponent: LabelComponent, HeaderComponent: HeaderComponent, dateRange: undefined, pivotCountRequest: 4, query: activeQuery, initialUniqueValues: uniqueValues[currentTable], disabled: !baseAst || !dataDisplayed, pivotRecommendationsEnabled: pivotRecommendationsEnabled && overrideRecommendations, report: tempReport }), pivot && (_jsx(PivotForm, { columns: columns, uniqueValues: uniqueValues[currentTable], setPivotRowField: (value) => {
2974
- setPivotRowField(value);
2975
- updatePivot(value, 'rowField');
2976
- }, setPivotColumnField: (value) => {
2977
- setPivotColumnField(value);
2978
- updatePivot(value, 'columnField');
2979
- }, setPivotValueField: (value) => {
2980
- setPivotValueField(value);
2981
- updatePivot(value, 'valueField');
2982
- }, setPivotAggregation: (value) => {
2983
- setPivotAggregation(value);
2984
- updatePivot(value, 'aggregationType');
2985
- }, onDelete: () => {
2986
- setPivot(null);
2987
- setPivotData([]);
2988
- const formattedRows = formatRows(rows, columns, false);
2989
- setFormattedRows(formattedRows);
2990
- }, pivotRowField: pivotRowField, pivotColumnField: pivotColumnField, pivotValueField: pivotValueField, pivotAggregation: pivotAggregation, SecondaryButtonComponent: SecondaryButtonComponent, SelectComponent: SelectComponent, PivotColumnContainer: PivotColumnContainer }))] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Sort" }), pivot && pivot.sort && (_jsx("div", { style: {
2991
- display: 'flex',
2992
- flexDirection: 'column',
2993
- gap: 8,
2994
- marginBottom: 12,
2995
- }, children: _jsx(SortSentence, { sortData: {
2996
- type: pivot.sortDirection,
2997
- expr: { type: 'column_ref', column: pivot.sortField },
2998
- }, columns: pivot
2999
- ? pivot.columnField
3000
- ? [`.${pivot.rowField}`]
3001
- : [
3002
- `.${pivot.rowField}`,
3003
- `.${pivot.valueField || 'count'}`,
3004
- ]
3005
- : selectedColumns, setIsPending: setIsPending, setEditPopoverKey: () => { }, setActiveEditItem: setActiveEditItem, setActivePath: setActivePath, setOpenPopover: setOpenPopover, SortPopover: SortPopoverComponent, EditPopover: AddSortPopover, handleDelete: async () => {
3006
- if (pivot) {
3007
- const tempPivot = { ...pivot, sort: false };
3008
- let dateBucket = undefined;
3009
- const tempDateRange = dateRanges &&
3010
- pivot.rowField &&
3011
- dateRanges[pivot.rowField];
3012
- if (tempDateRange) {
3013
- dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
3014
- }
3015
- setPivot(tempPivot);
3016
- const pivotedData = await generatePivotTable(tempPivot, rows, undefined, false, -1, undefined, dateBucket, tempReport, client, uniqueValues[currentTable]);
3017
- setPivotData(pivotedData || []);
3018
- const formattedRows = formatRows(pivotedData.rows, columns, true, pivot.aggregationType);
3019
- setFormattedRows(formattedRows);
3020
- setErrorMessage('');
3021
- return;
3022
- }
3023
- setBaseAst(deepCopy(baseAst));
3024
- fetchSqlQuery(deepCopy(baseAst));
3025
- }, onSave: async (column, direction) => {
3026
- if (pivot) {
3027
- const sortFieldType = column === (pivot.valueField || 'count')
3028
- ? 'number'
3029
- : pivot.rowFieldType;
3030
- const tempPivot = {
3031
- ...pivot,
3032
- sort: true,
3033
- sortDirection: direction,
3034
- sortField: column,
3035
- sortFieldType: sortFieldType,
3036
- };
3037
- setPivot(tempPivot);
3038
- let dateBucket = undefined;
3039
- const tempDateRange = dateRanges &&
3040
- pivot.rowField &&
3041
- dateRanges[pivot.rowField];
3042
- if (tempDateRange) {
3043
- dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
3044
- }
3045
- const pivotedData = await generatePivotTable(tempPivot, rows, undefined, false, -1, undefined, dateBucket, tempReport, client, uniqueValues[currentTable]);
3046
- setPivotData(pivotedData || []);
3047
- const formattedRows = formatRows(pivotedData.rows, columns, true, pivot.aggregationType);
3048
- setFormattedRows(formattedRows);
3049
- setErrorMessage('');
3050
- return;
3051
- }
3052
- setOpenPopover(null);
3053
- setBaseAst(deepCopy(baseAst));
3054
- fetchSqlQuery(deepCopy(baseAst));
3055
- }, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent }, `sort-sentence-pivot`) })), baseAst && baseAst.orderby && (_jsx("div", { style: {
3056
- display: 'flex',
3057
- flexDirection: 'column',
3058
- gap: 8,
3059
- marginBottom: 12,
3060
- }, children: baseAst.orderby.map((sortData, id) => (_jsx(SortSentence, { sortData: sortData, columns: selectedColumns, setIsPending: setIsPending, setEditPopoverKey: () => { }, setActiveEditItem: setActiveEditItem, setActivePath: setActivePath, setOpenPopover: setOpenPopover, SortPopover: SortPopoverComponent, EditPopover: AddSortPopover, handleDelete: () => {
3061
- if (pivot) {
3062
- setPivot({ ...pivot, sort: false });
3063
- return;
3064
- }
3065
- const newAst = { ...baseAst };
3066
- newAst.orderby.splice(id, 1);
3067
- setBaseAst(deepCopy(newAst));
3068
- fetchSqlQuery(deepCopy(newAst));
3069
- }, onSave: (column, direction) => {
3070
- if (pivot) {
3071
- const sortFieldType = column === (pivot.valueField || 'count')
3072
- ? 'number'
3073
- : pivot.rowFieldType;
3074
- setPivot({
3075
- ...pivot,
3076
- sort: true,
3077
- sortDirection: direction,
3078
- sortField: column,
3079
- sortFieldType: sortFieldType,
3080
- });
3081
- return;
3082
- }
3083
- setIsPending(false);
3084
- setActiveEditItem(null);
3085
- setOpenPopover(null);
3086
- if (column === '')
3087
- return;
3088
- const newAst = { ...baseAst };
3089
- newAst.orderby[id] = {
3090
- expr: {
3091
- type: 'column_ref',
3092
- table: null,
3093
- column: column,
3094
- },
3095
- type: direction,
3096
- };
3097
- // look through the columns
3098
- setActivePath(null);
3099
- setOpenPopover(null);
3100
- setBaseAst(deepCopy(newAst));
3101
- fetchSqlQuery(deepCopy(newAst));
3102
- }, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent }, `sort-sentence-${id}`))) })), _jsx(SecondaryButtonComponent, { disabled: !baseAst || !dataDisplayed, onClick: () => {
3103
- if (!selectedColumns || selectedColumns.length === 0) {
3104
- return;
3105
- }
3106
- if (!openPopover) {
3107
- setOpenPopover('AddSortPopover');
3108
- }
3109
- }, label: 'Add sort' }), _jsx("div", { style: {
3110
- position: 'relative',
3111
- ...(openPopover === 'AddSortPopover' && { top: 12 }),
3112
- }, children: _jsx(PopoverComponent, { isOpen: openPopover === 'AddSortPopover', setIsOpen: (isOpen) => {
3113
- if (!isOpen) {
3114
- setIsPending(false);
3115
- setActiveEditItem(null);
3116
- setActivePath(null);
3117
- setOpenPopover(null);
3118
- }
3119
- }, popoverTitle: "Sort by", popoverChildren: _jsx(AddSortPopover, { columns: pivot
3120
- ? pivot.columnField
3121
- ? [`.${pivot.rowField}`]
3122
- : [
3123
- `.${pivot.rowField}`,
3124
- `.${pivot.valueField || 'count'}`,
3125
- ]
3126
- : selectedColumns, Select: SelectComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, onSave: async (column, direction) => {
3127
- if (column === '')
3128
- return;
3129
- if (pivot) {
3130
- const sortFieldType = column === (pivot.valueField || 'count')
3131
- ? 'number'
3132
- : pivot.rowFieldType;
3133
- const tempPivot = {
3134
- ...pivot,
3135
- sort: true,
3136
- sortDirection: direction,
3137
- sortField: column,
3138
- sortFieldType: sortFieldType,
3139
- };
3140
- setPivot(tempPivot);
3141
- let dateBucket = undefined;
3142
- const tempDateRange = dateRanges &&
3143
- pivot.rowField &&
3144
- dateRanges[pivot.rowField];
3145
- if (tempDateRange) {
3146
- dateBucket = getDateBucketFromRange(tempDateRange.dateRange);
3147
- }
3148
- const pivotedData = await generatePivotTable(tempPivot, rows, undefined, false, -1, undefined, dateBucket, tempReport, client, uniqueValues[currentTable]);
3149
- setErrorMessage('');
3150
- setPivotData(pivotedData || []);
3151
- const formattedRows = formatRows(pivotedData.rows, columns, true, pivot.aggregationType);
3152
- setFormattedRows(formattedRows);
3153
- setActivePath(null);
3154
- setOpenPopover(null);
3155
- setBaseAst(deepCopy(baseAst));
3156
- return;
3157
- }
3158
- const newAst = { ...baseAst };
3159
- if (!newAst.orderby)
3160
- newAst.orderby = [];
3161
- newAst.orderby.push({
3162
- expr: { type: 'column_ref', column },
3163
- type: direction,
3164
- });
3165
- // look through the columns
3166
- setActivePath(null);
3167
- setOpenPopover(null);
3168
- setBaseAst(deepCopy(newAst));
3169
- fetchSqlQuery(deepCopy(newAst));
3170
- } }) }) })] }), _jsxs("div", { style: { width: '100%' }, children: [_jsx(SidebarHeadingComponent, { label: "Limit" }), baseAst && baseAst.limit && baseAst.limit.value?.length > 0 ? (_jsx("div", { style: {
3171
- display: 'flex',
3172
- flexDirection: 'column',
3173
- gap: 8,
3174
- marginBottom: 12,
3175
- }, children: _jsx(LimitSentence, { limit: baseAst.limit, setOpenPopover: setOpenPopover, LimitPopover: LimitPopoverComponent, EditPopover: AddLimitPopover, handleDelete: () => {
3176
- const newAst = { ...baseAst };
3177
- newAst.limit = null;
3178
- setBaseAst(deepCopy(newAst));
3179
- fetchSqlQuery(deepCopy(newAst));
3180
- }, onSave: (limit) => {
3181
- const newAst = { ...baseAst };
3182
- newAst.limit = {
3183
- seperator: '',
3184
- value: [
3185
- {
3186
- type: 'number',
3187
- value: limit,
3188
- },
3189
- ],
3190
- };
3191
- setOpenPopover(null);
3192
- setBaseAst(deepCopy(newAst));
3193
- fetchSqlQuery(deepCopy(newAst));
3194
- }, TextInput: TextInputComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent }) })) : (_jsxs(_Fragment, { children: [_jsx(SecondaryButtonComponent, { disabled: !baseAst || !dataDisplayed, onClick: () => {
3195
- if (!selectedColumns || selectedColumns.length === 0) {
3196
- return;
3197
- }
3198
- if (!baseAst) {
3199
- return;
3200
- }
3201
- if (!openPopover) {
3202
- setOpenPopover('AddLimitPopover');
3203
- }
3204
- }, label: 'Add limit' }), _jsx("div", { style: {
3205
- position: 'relative',
3206
- ...(openPopover === 'AddLimitPopover' && { top: 12 }),
3207
- }, children: _jsx(PopoverComponent, { isOpen: openPopover === 'AddLimitPopover', setIsOpen: (isOpen) => {
3208
- if (!isOpen) {
3209
- setIsPending(false);
3210
- setActiveEditItem(null);
3211
- setActivePath(null);
3212
- setOpenPopover(null);
3213
- }
3214
- }, popoverTitle: "Add limit", popoverChildren: _jsx(AddLimitPopover, { TextInput: TextInputComponent, Button: ButtonComponent, SecondaryButton: SecondaryButtonComponent, onSave: (limit) => {
3215
- const newAst = { ...baseAst };
3216
- newAst.limit = {
3217
- seperator: '',
3218
- value: [
3219
- {
3220
- type: 'number',
3221
- value: Number(limit),
3222
- },
3223
- ],
3224
- };
3225
- setOpenPopover(null);
3226
- setBaseAst(deepCopy(newAst));
3227
- fetchSqlQuery(deepCopy(newAst));
3228
- } }) }) })] }))] })] }), _jsxs(ContainerComponent, { children: [isAIEnabled && (_jsx("form", { ref: askAIContainerRef, onSubmit: (event) => {
3229
- event.preventDefault();
3230
- }, style: {
3231
- display: 'flex',
3232
- flexDirection: 'row',
3233
- gap: 12,
3234
- visibility: askAIInputWidth === -1 && askAILoadingContainerWidth === -1
3235
- ? 'hidden'
3236
- : 'visible',
3237
- }, children: _jsxs(_Fragment, { children: [_jsx(TextInputComponent, { id: "ask_ai_input_bar", value: aiPrompt, width: askAIInputWidth !== -1
3238
- ? askAIInputWidth
3239
- : askAILoadingContainerWidth, onChange: (e) => setAiPrompt(e.target.value), placeholder: askedAQuestion
3240
- ? 'Ask a follow-up question...'
3241
- : 'Ask a question...' }), _jsx(ButtonComponent, { onClick: handleAsk, label: 'Ask AI' }), ((baseAst && dataDisplayed) || initialLoad) && (_jsx(SecondaryButtonComponent, { label: 'New report', onClick: clearAllState }))] }) })), baseAst && (_jsx(TableComponent, { isLoading: tableLoading ||
3242
- (loading && errorMessage.length === 0) ||
3243
- initialChartLoad, rows: formattedRows, rowCount: pivot ? undefined : numberOfRows, columns: pivot
3244
- ? pivotData?.columns || emptyPivotColumns()
3245
- : enforceOrderOnColumns(Object.keys(rows[0] ?? {})).map((c) => {
3246
- return {
3247
- label: snakeAndCamelCaseToTitleCase(c),
3248
- field: c,
3249
- };
3250
- }), onPageChange: onPageChange, onSortChange: onSortChange })), _jsxs("div", { style: {
3251
- display: 'flex',
3252
- flexDirection: 'row',
3253
- gap: '12px',
3254
- width: '100%',
3255
- }, children: [errorMessage ? (_jsxs("div", { style: {
3256
- display: 'flex',
3257
- flexDirection: 'row',
3258
- overflow: 'hidden',
3259
- width: '100%',
3260
- gap: 12,
3261
- alignItems: 'center',
3262
- }, children: [_jsx(ErrorMessageComponent, { errorMessage: errorMessage }), _jsx(SecondaryButtonComponent, { onClick: handleAsk, label: 'Retry' })] })) : (_jsx("div", { style: { width: '100%' } })), baseAst && dataDisplayed && !initialChartLoad && (_jsxs(_Fragment, { children: [!hideCopySQL && (_jsx(SecondaryButtonComponent, { label: isCopying ? '✅ Copied' : 'Copy SQL', onClick: () => copySQLToClipboard() })), _jsx(ButtonComponent, { onClick: () => {
3263
- setIsChartBuilderOpen(true);
3264
- }, disabled: !!errorMessage, label: report ? 'Save changes' : 'Add to dashboard' })] }))] })] }), _jsx("style", { children: `body{margin:0;}` })] })), (!isChartBuilderHorizontalView || isChartBuilderOpen) && (_jsx(ChartBuilderWithModal, { report: report
3265
- ? {
3266
- ...report,
3267
- ...tempReport,
3268
- pivot: pivot,
3269
- yAxisFields: report.pivot && !pivot ? [] : report.yAxisFields,
3270
- columns: report.columns.filter((col) => {
3271
- return columns.find((c) => {
3272
- return col.field === c.field;
3273
- });
3274
- }),
3275
- queryString: activeQuery,
3276
- rows: rows,
3277
- }
3278
- : tempReport, rows: rows, columns: columns, pivot: pivot, query: activeQuery, showTableFormatOptions: showChartBuilderTableFormatOptions, showDateFieldOptions: isAdminEnabled, showAccessControlOptions: isAdminEnabled, title: report ? 'Save changes' : 'Add to dashboard', isHorizontalView: true, isOpen: isChartBuilderOpen, setIsOpen: setIsChartBuilderOpen, onAddToDashboardComplete: report ? onSubmitEditReport : onSubmitCreateReport, destinationDashboard: destinationDashboard, organizationName: organizationName, pivotData: pivotData, initialUniqueValues: uniqueValues[currentTable], pivotRecommendationsEnabled: pivotRecommendationsEnabled && overrideRecommendations, SelectComponent: SelectComponent, TextInputComponent: TextInputComponent, ButtonComponent: ButtonComponent, SecondaryButtonComponent: SecondaryButtonComponent, HeaderComponent: HeaderComponent, SubHeaderComponent: SubHeaderComponent, LabelComponent: LabelComponent, TextComponent: TextComponent, CardComponent: CardComponent, ModalComponent: ChartBuilderModalComponent, PopoverComponent: PopoverComponent, TableComponent: TableComponent, DeleteButtonComponent: DeleteButtonComponent, LoadingComponent: LoadingComponent, ChartBuilderInputRowContainer: ChartBuilderInputRowContainer, ChartBuilderInputColumnContainer: ChartBuilderInputColumnContainer, FormContainer: ChartBuilderFormContainer, hideDateRangeFilter: true, buttonLabel: report ? 'Save changes' : 'Add to dashboard', onClickChartElement: onClickChartElement, rowCount: numberOfRows, onPageChange: onPageChange, onSortChange: onSortChange, isLoading: tableLoading }))] }));
3279
- }