terrier-engine 4.4.0 → 4.4.3

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 (313) hide show
  1. package/data-dive/dd-app.ts +45 -0
  2. package/data-dive/dd-db.ts +12 -0
  3. package/data-dive/dd-routes.ts +10 -0
  4. package/data-dive/dd-session.ts +45 -0
  5. package/data-dive/dd-user.ts +12 -0
  6. package/data-dive/dives/dive-editor.ts +244 -0
  7. package/data-dive/dives/dive-list.ts +164 -0
  8. package/data-dive/dives/dive-runs.ts +272 -0
  9. package/data-dive/dives/dive-settings.ts +227 -0
  10. package/data-dive/dives/dives.ts +81 -0
  11. package/data-dive/dives/group-editor.ts +139 -0
  12. package/data-dive/gen/models.ts +192 -0
  13. package/data-dive/queries/columns.ts +355 -0
  14. package/data-dive/queries/dates.ts +286 -0
  15. package/data-dive/queries/filters.ts +723 -0
  16. package/data-dive/queries/queries.ts +263 -0
  17. package/data-dive/queries/query-editor.ts +232 -0
  18. package/data-dive/queries/query-form.ts +51 -0
  19. package/data-dive/queries/tables.ts +395 -0
  20. package/data-dive/queries/test/dates.test.ts +178 -0
  21. package/package.json +1 -1
  22. package/{parts → terrier/parts}/panel-part.ts +5 -1
  23. package/tsconfig.json +21 -0
  24. /package/{README.md → terrier/README.md} +0 -0
  25. /package/{api.ts → terrier/api.ts} +0 -0
  26. /package/{app.ts → terrier/app.ts} +0 -0
  27. /package/{attachments.ts → terrier/attachments.ts} +0 -0
  28. /package/{db-client.ts → terrier/db-client.ts} +0 -0
  29. /package/{dropdowns.ts → terrier/dropdowns.ts} +0 -0
  30. /package/{format.ts → terrier/format.ts} +0 -0
  31. /package/{forms.ts → terrier/forms.ts} +0 -0
  32. /package/{fragments.ts → terrier/fragments.ts} +0 -0
  33. /package/{gen → terrier/gen}/hub-icons.ts +0 -0
  34. /package/{glyps.ts → terrier/glyps.ts} +0 -0
  35. /package/{ids.ts → terrier/ids.ts} +0 -0
  36. /package/{images → terrier/images}/icons/active.svg +0 -0
  37. /package/{images → terrier/images}/icons/admin.svg +0 -0
  38. /package/{images → terrier/images}/icons/archive.svg +0 -0
  39. /package/{images → terrier/images}/icons/arrow_down.svg +0 -0
  40. /package/{images → terrier/images}/icons/arrow_left.svg +0 -0
  41. /package/{images → terrier/images}/icons/arrow_right.svg +0 -0
  42. /package/{images → terrier/images}/icons/arrow_up.svg +0 -0
  43. /package/{images → terrier/images}/icons/assign.svg +0 -0
  44. /package/{images → terrier/images}/icons/attachment.svg +0 -0
  45. /package/{images → terrier/images}/icons/back.svg +0 -0
  46. /package/{images → terrier/images}/icons/badge.svg +0 -0
  47. /package/{images → terrier/images}/icons/board.svg +0 -0
  48. /package/{images → terrier/images}/icons/branch.svg +0 -0
  49. /package/{images → terrier/images}/icons/bug.svg +0 -0
  50. /package/{images → terrier/images}/icons/calculator.svg +0 -0
  51. /package/{images → terrier/images}/icons/checkmark.svg +0 -0
  52. /package/{images → terrier/images}/icons/close.svg +0 -0
  53. /package/{images → terrier/images}/icons/clypboard.svg +0 -0
  54. /package/{images → terrier/images}/icons/comment.svg +0 -0
  55. /package/{images → terrier/images}/icons/complete.svg +0 -0
  56. /package/{images → terrier/images}/icons/dashboard.svg +0 -0
  57. /package/{images → terrier/images}/icons/data_pull.svg +0 -0
  58. /package/{images → terrier/images}/icons/data_update.svg +0 -0
  59. /package/{images → terrier/images}/icons/database.svg +0 -0
  60. /package/{images → terrier/images}/icons/day.svg +0 -0
  61. /package/{images → terrier/images}/icons/delete.svg +0 -0
  62. /package/{images → terrier/images}/icons/documentation.svg +0 -0
  63. /package/{images → terrier/images}/icons/edit.svg +0 -0
  64. /package/{images → terrier/images}/icons/feature.svg +0 -0
  65. /package/{images → terrier/images}/icons/flex.svg +0 -0
  66. /package/{images → terrier/images}/icons/forward.svg +0 -0
  67. /package/{images → terrier/images}/icons/github.svg +0 -0
  68. /package/{images → terrier/images}/icons/history.svg +0 -0
  69. /package/{images → terrier/images}/icons/home.svg +0 -0
  70. /package/{images → terrier/images}/icons/image.svg +0 -0
  71. /package/{images → terrier/images}/icons/inbox.svg +0 -0
  72. /package/{images → terrier/images}/icons/info.svg +0 -0
  73. /package/{images → terrier/images}/icons/issue.svg +0 -0
  74. /package/{images → terrier/images}/icons/lane.svg +0 -0
  75. /package/{images → terrier/images}/icons/lane_asap.svg +0 -0
  76. /package/{images → terrier/images}/icons/lane_days.svg +0 -0
  77. /package/{images → terrier/images}/icons/lane_hours.svg +0 -0
  78. /package/{images → terrier/images}/icons/lane_weeks.svg +0 -0
  79. /package/{images → terrier/images}/icons/lanes_board.svg +0 -0
  80. /package/{images → terrier/images}/icons/level_complete.svg +0 -0
  81. /package/{images → terrier/images}/icons/level_highway.svg +0 -0
  82. /package/{images → terrier/images}/icons/level_on_ramp.svg +0 -0
  83. /package/{images → terrier/images}/icons/level_parking.svg +0 -0
  84. /package/{images → terrier/images}/icons/minus.svg +0 -0
  85. /package/{images → terrier/images}/icons/night.svg +0 -0
  86. /package/{images → terrier/images}/icons/origin.svg +0 -0
  87. /package/{images → terrier/images}/icons/pending.svg +0 -0
  88. /package/{images → terrier/images}/icons/plus.svg +0 -0
  89. /package/{images → terrier/images}/icons/post.svg +0 -0
  90. /package/{images → terrier/images}/icons/pr_closed.svg +0 -0
  91. /package/{images → terrier/images}/icons/pr_merged.svg +0 -0
  92. /package/{images → terrier/images}/icons/pr_open.svg +0 -0
  93. /package/{images → terrier/images}/icons/prioritized.svg +0 -0
  94. /package/{images → terrier/images}/icons/project.svg +0 -0
  95. /package/{images → terrier/images}/icons/question.svg +0 -0
  96. /package/{images → terrier/images}/icons/reaction.svg +0 -0
  97. /package/{images → terrier/images}/icons/recent.svg +0 -0
  98. /package/{images → terrier/images}/icons/refresh.svg +0 -0
  99. /package/{images → terrier/images}/icons/request.svg +0 -0
  100. /package/{images → terrier/images}/icons/settings.svg +0 -0
  101. /package/{images → terrier/images}/icons/status.svg +0 -0
  102. /package/{images → terrier/images}/icons/step_deploy.svg +0 -0
  103. /package/{images → terrier/images}/icons/step_develop.svg +0 -0
  104. /package/{images → terrier/images}/icons/step_investigate.svg +0 -0
  105. /package/{images → terrier/images}/icons/step_review.svg +0 -0
  106. /package/{images → terrier/images}/icons/step_test.svg +0 -0
  107. /package/{images → terrier/images}/icons/steps.svg +0 -0
  108. /package/{images → terrier/images}/icons/steps_board.svg +0 -0
  109. /package/{images → terrier/images}/icons/subscribe.svg +0 -0
  110. /package/{images → terrier/images}/icons/support.svg +0 -0
  111. /package/{images → terrier/images}/icons/terrier.svg +0 -0
  112. /package/{images → terrier/images}/icons/thumbs_up.svg +0 -0
  113. /package/{images → terrier/images}/icons/type.svg +0 -0
  114. /package/{images → terrier/images}/icons/unprioritized.svg +0 -0
  115. /package/{images → terrier/images}/icons/upload.svg +0 -0
  116. /package/{images → terrier/images}/icons/user.svg +0 -0
  117. /package/{images → terrier/images}/icons/users.svg +0 -0
  118. /package/{images → terrier/images}/optimized/icon-active.svg +0 -0
  119. /package/{images → terrier/images}/optimized/icon-admin.svg +0 -0
  120. /package/{images → terrier/images}/optimized/icon-archive.svg +0 -0
  121. /package/{images → terrier/images}/optimized/icon-arrow_down.svg +0 -0
  122. /package/{images → terrier/images}/optimized/icon-arrow_left.svg +0 -0
  123. /package/{images → terrier/images}/optimized/icon-arrow_right.svg +0 -0
  124. /package/{images → terrier/images}/optimized/icon-arrow_up.svg +0 -0
  125. /package/{images → terrier/images}/optimized/icon-assign.svg +0 -0
  126. /package/{images → terrier/images}/optimized/icon-attachment.svg +0 -0
  127. /package/{images → terrier/images}/optimized/icon-back.svg +0 -0
  128. /package/{images → terrier/images}/optimized/icon-badge.svg +0 -0
  129. /package/{images → terrier/images}/optimized/icon-board.svg +0 -0
  130. /package/{images → terrier/images}/optimized/icon-branch.svg +0 -0
  131. /package/{images → terrier/images}/optimized/icon-bug.svg +0 -0
  132. /package/{images → terrier/images}/optimized/icon-calculator.svg +0 -0
  133. /package/{images → terrier/images}/optimized/icon-checkmark.svg +0 -0
  134. /package/{images → terrier/images}/optimized/icon-close.svg +0 -0
  135. /package/{images → terrier/images}/optimized/icon-clypboard.svg +0 -0
  136. /package/{images → terrier/images}/optimized/icon-comment.svg +0 -0
  137. /package/{images → terrier/images}/optimized/icon-complete.svg +0 -0
  138. /package/{images → terrier/images}/optimized/icon-dashboard.svg +0 -0
  139. /package/{images → terrier/images}/optimized/icon-data_pull.svg +0 -0
  140. /package/{images → terrier/images}/optimized/icon-data_update.svg +0 -0
  141. /package/{images → terrier/images}/optimized/icon-database.svg +0 -0
  142. /package/{images → terrier/images}/optimized/icon-day.svg +0 -0
  143. /package/{images → terrier/images}/optimized/icon-delete.svg +0 -0
  144. /package/{images → terrier/images}/optimized/icon-documentation.svg +0 -0
  145. /package/{images → terrier/images}/optimized/icon-edit.svg +0 -0
  146. /package/{images → terrier/images}/optimized/icon-feature.svg +0 -0
  147. /package/{images → terrier/images}/optimized/icon-flex.svg +0 -0
  148. /package/{images → terrier/images}/optimized/icon-forward.svg +0 -0
  149. /package/{images → terrier/images}/optimized/icon-github.svg +0 -0
  150. /package/{images → terrier/images}/optimized/icon-history.svg +0 -0
  151. /package/{images → terrier/images}/optimized/icon-home.svg +0 -0
  152. /package/{images → terrier/images}/optimized/icon-image.svg +0 -0
  153. /package/{images → terrier/images}/optimized/icon-inbox.svg +0 -0
  154. /package/{images → terrier/images}/optimized/icon-info.svg +0 -0
  155. /package/{images → terrier/images}/optimized/icon-issue.svg +0 -0
  156. /package/{images → terrier/images}/optimized/icon-lane.svg +0 -0
  157. /package/{images → terrier/images}/optimized/icon-lane_asap.svg +0 -0
  158. /package/{images → terrier/images}/optimized/icon-lane_days.svg +0 -0
  159. /package/{images → terrier/images}/optimized/icon-lane_hours.svg +0 -0
  160. /package/{images → terrier/images}/optimized/icon-lane_weeks.svg +0 -0
  161. /package/{images → terrier/images}/optimized/icon-lanes_board.svg +0 -0
  162. /package/{images → terrier/images}/optimized/icon-level_complete.svg +0 -0
  163. /package/{images → terrier/images}/optimized/icon-level_highway.svg +0 -0
  164. /package/{images → terrier/images}/optimized/icon-level_on_ramp.svg +0 -0
  165. /package/{images → terrier/images}/optimized/icon-level_parking.svg +0 -0
  166. /package/{images → terrier/images}/optimized/icon-minus.svg +0 -0
  167. /package/{images → terrier/images}/optimized/icon-night.svg +0 -0
  168. /package/{images → terrier/images}/optimized/icon-origin.svg +0 -0
  169. /package/{images → terrier/images}/optimized/icon-pending.svg +0 -0
  170. /package/{images → terrier/images}/optimized/icon-plus.svg +0 -0
  171. /package/{images → terrier/images}/optimized/icon-post.svg +0 -0
  172. /package/{images → terrier/images}/optimized/icon-pr_closed.svg +0 -0
  173. /package/{images → terrier/images}/optimized/icon-pr_merged.svg +0 -0
  174. /package/{images → terrier/images}/optimized/icon-pr_open.svg +0 -0
  175. /package/{images → terrier/images}/optimized/icon-prioritized.svg +0 -0
  176. /package/{images → terrier/images}/optimized/icon-project.svg +0 -0
  177. /package/{images → terrier/images}/optimized/icon-question.svg +0 -0
  178. /package/{images → terrier/images}/optimized/icon-reaction.svg +0 -0
  179. /package/{images → terrier/images}/optimized/icon-recent.svg +0 -0
  180. /package/{images → terrier/images}/optimized/icon-refresh.svg +0 -0
  181. /package/{images → terrier/images}/optimized/icon-request.svg +0 -0
  182. /package/{images → terrier/images}/optimized/icon-settings.svg +0 -0
  183. /package/{images → terrier/images}/optimized/icon-status.svg +0 -0
  184. /package/{images → terrier/images}/optimized/icon-step_deploy.svg +0 -0
  185. /package/{images → terrier/images}/optimized/icon-step_develop.svg +0 -0
  186. /package/{images → terrier/images}/optimized/icon-step_investigate.svg +0 -0
  187. /package/{images → terrier/images}/optimized/icon-step_review.svg +0 -0
  188. /package/{images → terrier/images}/optimized/icon-step_test.svg +0 -0
  189. /package/{images → terrier/images}/optimized/icon-steps.svg +0 -0
  190. /package/{images → terrier/images}/optimized/icon-steps_board.svg +0 -0
  191. /package/{images → terrier/images}/optimized/icon-subscribe.svg +0 -0
  192. /package/{images → terrier/images}/optimized/icon-support.svg +0 -0
  193. /package/{images → terrier/images}/optimized/icon-terrier.svg +0 -0
  194. /package/{images → terrier/images}/optimized/icon-thumbs_up.svg +0 -0
  195. /package/{images → terrier/images}/optimized/icon-type.svg +0 -0
  196. /package/{images → terrier/images}/optimized/icon-unprioritized.svg +0 -0
  197. /package/{images → terrier/images}/optimized/icon-upload.svg +0 -0
  198. /package/{images → terrier/images}/optimized/icon-user.svg +0 -0
  199. /package/{images → terrier/images}/optimized/icon-users.svg +0 -0
  200. /package/{images → terrier/images}/optimized/terrier-hub-favicon.svg +0 -0
  201. /package/{images → terrier/images}/optimized/terrier-hub-icon-dark.svg +0 -0
  202. /package/{images → terrier/images}/optimized/terrier-hub-icon-light.svg +0 -0
  203. /package/{images → terrier/images}/optimized/terrier-hub-loader.svg +0 -0
  204. /package/{images → terrier/images}/optimized/terrier-hub-logo-dark.svg +0 -0
  205. /package/{images → terrier/images}/optimized/terrier-hub-logo-light.svg +0 -0
  206. /package/{images → terrier/images}/raw/icon-active.svg +0 -0
  207. /package/{images → terrier/images}/raw/icon-admin.svg +0 -0
  208. /package/{images → terrier/images}/raw/icon-archive.svg +0 -0
  209. /package/{images → terrier/images}/raw/icon-arrow_down.svg +0 -0
  210. /package/{images → terrier/images}/raw/icon-arrow_left.svg +0 -0
  211. /package/{images → terrier/images}/raw/icon-arrow_right.svg +0 -0
  212. /package/{images → terrier/images}/raw/icon-arrow_up.svg +0 -0
  213. /package/{images → terrier/images}/raw/icon-assign.svg +0 -0
  214. /package/{images → terrier/images}/raw/icon-attachment.svg +0 -0
  215. /package/{images → terrier/images}/raw/icon-back.svg +0 -0
  216. /package/{images → terrier/images}/raw/icon-badge.svg +0 -0
  217. /package/{images → terrier/images}/raw/icon-board.svg +0 -0
  218. /package/{images → terrier/images}/raw/icon-branch.svg +0 -0
  219. /package/{images → terrier/images}/raw/icon-bug.svg +0 -0
  220. /package/{images → terrier/images}/raw/icon-calculator.svg +0 -0
  221. /package/{images → terrier/images}/raw/icon-checkmark.svg +0 -0
  222. /package/{images → terrier/images}/raw/icon-close.svg +0 -0
  223. /package/{images → terrier/images}/raw/icon-clypboard.svg +0 -0
  224. /package/{images → terrier/images}/raw/icon-comment.svg +0 -0
  225. /package/{images → terrier/images}/raw/icon-complete.svg +0 -0
  226. /package/{images → terrier/images}/raw/icon-dashboard.svg +0 -0
  227. /package/{images → terrier/images}/raw/icon-data_pull.svg +0 -0
  228. /package/{images → terrier/images}/raw/icon-data_update.svg +0 -0
  229. /package/{images → terrier/images}/raw/icon-database.svg +0 -0
  230. /package/{images → terrier/images}/raw/icon-day.svg +0 -0
  231. /package/{images → terrier/images}/raw/icon-delete.svg +0 -0
  232. /package/{images → terrier/images}/raw/icon-documentation.svg +0 -0
  233. /package/{images → terrier/images}/raw/icon-edit.svg +0 -0
  234. /package/{images → terrier/images}/raw/icon-feature.svg +0 -0
  235. /package/{images → terrier/images}/raw/icon-flex.svg +0 -0
  236. /package/{images → terrier/images}/raw/icon-forward.svg +0 -0
  237. /package/{images → terrier/images}/raw/icon-github.svg +0 -0
  238. /package/{images → terrier/images}/raw/icon-history.svg +0 -0
  239. /package/{images → terrier/images}/raw/icon-home.svg +0 -0
  240. /package/{images → terrier/images}/raw/icon-image.svg +0 -0
  241. /package/{images → terrier/images}/raw/icon-inbox.svg +0 -0
  242. /package/{images → terrier/images}/raw/icon-info.svg +0 -0
  243. /package/{images → terrier/images}/raw/icon-issue.svg +0 -0
  244. /package/{images → terrier/images}/raw/icon-lane.svg +0 -0
  245. /package/{images → terrier/images}/raw/icon-lane_asap.svg +0 -0
  246. /package/{images → terrier/images}/raw/icon-lane_days.svg +0 -0
  247. /package/{images → terrier/images}/raw/icon-lane_hours.svg +0 -0
  248. /package/{images → terrier/images}/raw/icon-lane_weeks.svg +0 -0
  249. /package/{images → terrier/images}/raw/icon-lanes_board.svg +0 -0
  250. /package/{images → terrier/images}/raw/icon-level_complete.svg +0 -0
  251. /package/{images → terrier/images}/raw/icon-level_highway.svg +0 -0
  252. /package/{images → terrier/images}/raw/icon-level_on_ramp.svg +0 -0
  253. /package/{images → terrier/images}/raw/icon-level_parking.svg +0 -0
  254. /package/{images → terrier/images}/raw/icon-minus.svg +0 -0
  255. /package/{images → terrier/images}/raw/icon-night.svg +0 -0
  256. /package/{images → terrier/images}/raw/icon-origin.svg +0 -0
  257. /package/{images → terrier/images}/raw/icon-pending.svg +0 -0
  258. /package/{images → terrier/images}/raw/icon-plus.svg +0 -0
  259. /package/{images → terrier/images}/raw/icon-post.svg +0 -0
  260. /package/{images → terrier/images}/raw/icon-pr_closed.svg +0 -0
  261. /package/{images → terrier/images}/raw/icon-pr_merged.svg +0 -0
  262. /package/{images → terrier/images}/raw/icon-pr_open.svg +0 -0
  263. /package/{images → terrier/images}/raw/icon-prioritized.svg +0 -0
  264. /package/{images → terrier/images}/raw/icon-project.svg +0 -0
  265. /package/{images → terrier/images}/raw/icon-question.svg +0 -0
  266. /package/{images → terrier/images}/raw/icon-reaction.svg +0 -0
  267. /package/{images → terrier/images}/raw/icon-recent.svg +0 -0
  268. /package/{images → terrier/images}/raw/icon-refresh.svg +0 -0
  269. /package/{images → terrier/images}/raw/icon-request.svg +0 -0
  270. /package/{images → terrier/images}/raw/icon-settings.svg +0 -0
  271. /package/{images → terrier/images}/raw/icon-status.svg +0 -0
  272. /package/{images → terrier/images}/raw/icon-step_deploy.svg +0 -0
  273. /package/{images → terrier/images}/raw/icon-step_develop.svg +0 -0
  274. /package/{images → terrier/images}/raw/icon-step_investigate.svg +0 -0
  275. /package/{images → terrier/images}/raw/icon-step_review.svg +0 -0
  276. /package/{images → terrier/images}/raw/icon-step_test.svg +0 -0
  277. /package/{images → terrier/images}/raw/icon-steps.svg +0 -0
  278. /package/{images → terrier/images}/raw/icon-steps_board.svg +0 -0
  279. /package/{images → terrier/images}/raw/icon-subscribe.svg +0 -0
  280. /package/{images → terrier/images}/raw/icon-support.svg +0 -0
  281. /package/{images → terrier/images}/raw/icon-terrier.svg +0 -0
  282. /package/{images → terrier/images}/raw/icon-thumbs_up.svg +0 -0
  283. /package/{images → terrier/images}/raw/icon-type.svg +0 -0
  284. /package/{images → terrier/images}/raw/icon-unprioritized.svg +0 -0
  285. /package/{images → terrier/images}/raw/icon-upload.svg +0 -0
  286. /package/{images → terrier/images}/raw/icon-user.svg +0 -0
  287. /package/{images → terrier/images}/raw/icon-users.svg +0 -0
  288. /package/{images → terrier/images}/raw/terrier-hub-favicon-alert.png +0 -0
  289. /package/{images → terrier/images}/raw/terrier-hub-favicon-dark.png +0 -0
  290. /package/{images → terrier/images}/raw/terrier-hub-favicon.png +0 -0
  291. /package/{images → terrier/images}/raw/terrier-hub-favicon.svg +0 -0
  292. /package/{images → terrier/images}/raw/terrier-hub-icon-dark.svg +0 -0
  293. /package/{images → terrier/images}/raw/terrier-hub-icon-light.png +0 -0
  294. /package/{images → terrier/images}/raw/terrier-hub-icon-light.svg +0 -0
  295. /package/{images → terrier/images}/raw/terrier-hub-loader.svg +0 -0
  296. /package/{images → terrier/images}/raw/terrier-hub-logo-dark.svg +0 -0
  297. /package/{images → terrier/images}/raw/terrier-hub-logo-light.png +0 -0
  298. /package/{images → terrier/images}/raw/terrier-hub-logo-light.svg +0 -0
  299. /package/{lightbox.ts → terrier/lightbox.ts} +0 -0
  300. /package/{loading.ts → terrier/loading.ts} +0 -0
  301. /package/{modals.ts → terrier/modals.ts} +0 -0
  302. /package/{overlays.ts → terrier/overlays.ts} +0 -0
  303. /package/{parts → terrier/parts}/content-part.ts +0 -0
  304. /package/{parts → terrier/parts}/not-found-page.ts +0 -0
  305. /package/{parts → terrier/parts}/page-part.ts +0 -0
  306. /package/{parts → terrier/parts}/terrier-form-part.ts +0 -0
  307. /package/{parts → terrier/parts}/terrier-part.ts +0 -0
  308. /package/{schema.ts → terrier/schema.ts} +0 -0
  309. /package/{sheets.ts → terrier/sheets.ts} +0 -0
  310. /package/{tabs.ts → terrier/tabs.ts} +0 -0
  311. /package/{theme.ts → terrier/theme.ts} +0 -0
  312. /package/{toasts.ts → terrier/toasts.ts} +0 -0
  313. /package/{tooltips.ts → terrier/tooltips.ts} +0 -0
@@ -0,0 +1,272 @@
1
+ import {ModalPart} from "../../terrier/modals"
2
+ import {PartTag} from "tuff-core/parts"
3
+ import {DdDive, DdDiveRun, UnpersistedDdDiveRun} from "../gen/models"
4
+ import Db from "../dd-db"
5
+ import Api, {ErrorEvent} from "../../terrier/api"
6
+ import {Query} from "../queries/queries"
7
+ import {DivTag, HtmlParentTag} from "tuff-core/html"
8
+ import {messages} from "tuff-core"
9
+ import {IconName} from "../../terrier/theme"
10
+ import Filters, {DateRangeFilter, DirectFilter, FilterInput, InclusionFilter} from "../queries/filters"
11
+ import Dives from "./dives"
12
+ import {Logger} from "tuff-core/logging";
13
+ import Schema, {SchemaDef} from "../../terrier/schema"
14
+ import {TerrierFormFields} from "../../terrier/forms"
15
+ import inflection from "inflection"
16
+ import Dates, {DateLiteral, DatePeriodPickerPart, DatePeriodPickerState, LiteralDateRange} from "../queries/dates"
17
+ import dayjs from "dayjs"
18
+
19
+ const log = new Logger("DiveRuns")
20
+
21
+ type RunQueryResult = {
22
+ id: string
23
+ time: string
24
+ status: 'pending' | 'success' | 'error'
25
+ message?: string
26
+ }
27
+
28
+ const statusIcons: Record<RunQueryResult['status'], IconName> = {
29
+ pending: 'glyp-pending',
30
+ success: 'glyp-complete',
31
+ error: 'glyp-alert'
32
+ }
33
+
34
+ type RunFileOutput = {
35
+ name: string
36
+ size: number
37
+ url: string
38
+ }
39
+
40
+ export class DiveRunModal extends ModalPart<{dive: DdDive }> {
41
+
42
+ schema!: SchemaDef
43
+ filters!: FilterInput[]
44
+ run?: DdDiveRun
45
+ error?: ErrorEvent
46
+ inputFields!: TerrierFormFields<any>
47
+ fileOutput?: RunFileOutput
48
+ queryResults: Record<string, RunQueryResult> = {}
49
+
50
+ startKey = messages.untypedKey()
51
+ pickDateKey = messages.typedKey<{ input_key: string }>()
52
+
53
+ async init() {
54
+ this.setTitle("Run Dive")
55
+ this.setIcon('glyp-play')
56
+
57
+ this.schema = await Schema.get()
58
+
59
+ // initialize the inputs
60
+ this.filters = Dives.computeFilterInputs(this.schema, this.state.dive)
61
+ const rawInputs = Filters.populateRawInputData(this.filters)
62
+ this.inputFields = new TerrierFormFields<any>(this, rawInputs)
63
+
64
+ this.listen('datachanged', this.inputFields.dataChangedKey, _ => {
65
+ this.updateFilters().then()
66
+ })
67
+ await this.updateFilters()
68
+
69
+ this.addAction({
70
+ title: "Run",
71
+ icon: 'glyp-play',
72
+ click: {key: this.startKey}
73
+ })
74
+
75
+ this.onClick(this.startKey, _ => {
76
+ this.startActionLoading()
77
+ this.createRun()
78
+ })
79
+
80
+ this.onClick(this.pickDateKey, m => {
81
+ const initialRange = {
82
+ min: this.inputFields.data[`${m.data.input_key}-min`] as DateLiteral,
83
+ max: dayjs(this.inputFields.data[`${m.data.input_key}-max`]).add(1, 'day').format(Dates.literalFormat) as DateLiteral
84
+ } as LiteralDateRange
85
+ this.toggleDropdown(DatePeriodPickerPart, {
86
+ initial: initialRange,
87
+ callback: (newRange: LiteralDateRange) => {
88
+ log.info(`Picked date range for ${m.data.input_key}`, newRange)
89
+ this.inputFields.data[`${m.data.input_key}-min`] = newRange.min
90
+ this.inputFields.data[`${m.data.input_key}-max`] = dayjs(newRange.max).subtract(1, 'day').format(Dates.literalFormat)
91
+ this.dirty()
92
+ }} as DatePeriodPickerState,
93
+ m.event.target
94
+ )
95
+ })
96
+
97
+ this.dirty()
98
+ }
99
+
100
+ async updateFilters() {
101
+ const raw = await this.inputFields.serialize()
102
+ Filters.serializeRawInputData(this.filters, raw)
103
+ log.info(`Raw and serialized input data`, raw, this.filters)
104
+ log.info(`Filter input data:`, this.inputFields.data)
105
+ }
106
+
107
+ async createRun() {
108
+ const newRun: UnpersistedDdDiveRun = {
109
+ dd_dive_id: this.state.dive.id,
110
+ status: 'initial',
111
+ input_data: {
112
+ filters: this.filters,
113
+ ...this.state.dive.query_data!
114
+ }
115
+ }
116
+ const res = await Db().upsert('dd_dive_run', newRun)
117
+ if (res.status == 'success' && res.record) {
118
+ this.beginStreaming(res.record)
119
+ } else {
120
+ this.alertToast(`Error creating dive run: ${res.message}`)
121
+ }
122
+ this.dirty()
123
+ }
124
+
125
+ beginStreaming(run: DdDiveRun) {
126
+ this.run = run
127
+ Api.stream(`/data_dive/stream_run/${this.run.id}`)
128
+ .on<RunQueryResult>('query_result', res => {
129
+ this.queryResults[res.id] = res
130
+ this.dirty()
131
+ })
132
+ .on<RunFileOutput>('file_output', res => {
133
+ this.fileOutput = res
134
+ this.stopActionLoading()
135
+ this.dirty()
136
+ })
137
+ .onError(evt => {
138
+ this.error = evt
139
+ this.stopActionLoading()
140
+ this.dirty()
141
+ })
142
+ .onClose(() => {
143
+ this.stopActionLoading()
144
+ this.dirty()
145
+ })
146
+ }
147
+
148
+
149
+ renderContent(parent: PartTag): void {
150
+ parent.div('.tt-flex.collapsible.padded.gap.tt-form', row => {
151
+ // inputs
152
+ row.div('.tt-flex.column.shrink.dd-dive-run-inputs', col => {
153
+ for (const filter of this.filters) {
154
+ this.renderInput(col, filter)
155
+ }
156
+ })
157
+
158
+ // output
159
+ row.div('.dd-dive-run-output', col => {
160
+ // error
161
+ if (this.error) {
162
+ col.div('.tt-bubble.alert', bubble => {
163
+ const error = this.error!
164
+ bubble.div('.message').text(error.message)
165
+ if (error.backtrace?.length) {
166
+ bubble.div('.backtrace', backtrace => {
167
+ for (const line of error.backtrace) {
168
+ backtrace.div('.line').text(line)
169
+ }
170
+ })
171
+ }
172
+ })
173
+ return
174
+ }
175
+
176
+ // queries
177
+ for (const query of this.state.dive.query_data?.queries || []) {
178
+ this.renderQuery(col, query)
179
+ }
180
+
181
+ // output
182
+ if (this.fileOutput) {
183
+ this.renderFileOutput(col, this.fileOutput)
184
+ }
185
+ })
186
+ })
187
+ }
188
+
189
+ renderQuery(parent: DivTag, query: Query) {
190
+ const res = this.queryResults[query.id]
191
+ const status = res?.status || 'pending'
192
+
193
+ parent.div('.query-run', status, row => {
194
+ row.i(statusIcons[status])
195
+ row.div('.name').text(query.name)
196
+ if (res?.status == 'error') {
197
+ row.div('.tt-bubble.alert').text(res.message || "Error!")
198
+ }
199
+ else if (res?.message?.length) {
200
+ row.div('.details').text(res.message)
201
+ }
202
+ })
203
+ }
204
+
205
+ renderFileOutput(parent: DivTag, fileOutput: RunFileOutput) {
206
+ parent.a('.file-output', {href: fileOutput.url}, row => {
207
+ row.i('.glyp-file_spreadsheet')
208
+ row.div('.name').text(fileOutput.name)
209
+ row.div('.details.glyp-download').text("Click to download")
210
+ })
211
+ }
212
+
213
+ renderInput(parent: HtmlParentTag, filter: FilterInput) {
214
+ parent.div('.dd-dive-run-input', col => {
215
+ // don't show anything after the # and replace periods with spaces
216
+ const title = inflection.titleize(filter.input_key.split('#')[0].split('.').join(' '))
217
+ col.label('.caption-size').text(title)
218
+ switch (filter.filter_type) {
219
+ case 'direct':
220
+ return this.renderDirectInput(col, filter)
221
+ case 'date_range':
222
+ return this.renderDateRangeInput(col, filter)
223
+ case 'inclusion':
224
+ return this.renderInclusionInput(col, filter)
225
+ default:
226
+ col.p().text(`Don't know how to render input for ${filter.filter_type} filter`)
227
+ }
228
+ })
229
+ }
230
+
231
+ renderDirectInput(parent: HtmlParentTag, filter: DirectFilter & FilterInput) {
232
+ parent.div('.tt-compound-field', field => {
233
+ field.label().text(Filters.operatorDisplay(filter.operator))
234
+ switch (filter.column_type) {
235
+ case 'cents':
236
+ field.label().text('$')
237
+ this.inputFields.numberInput(field, filter.input_key)
238
+ break
239
+ case 'number':
240
+ this.inputFields.numberInput(field, filter.input_key)
241
+ break
242
+ default:
243
+ this.inputFields.textInput(field, filter.input_key)
244
+ }
245
+ })
246
+ }
247
+
248
+ renderDateRangeInput(parent: HtmlParentTag, filter: DateRangeFilter & FilterInput) {
249
+ parent.div('.tt-compound-field', field => {
250
+ this.inputFields.dateInput(field, `${filter.input_key}-min`)
251
+ field.label().text('→')
252
+ this.inputFields.dateInput(field, `${filter.input_key}-max`)
253
+ field.a('.icon-only', {title: "Pick a common date range"}, a => {
254
+ a.i('.glyp-pick_date')
255
+ })
256
+ .emitClick(this.pickDateKey, {input_key: filter.input_key})
257
+
258
+ })
259
+ }
260
+
261
+ renderInclusionInput(parent: HtmlParentTag, filter: InclusionFilter & FilterInput) {
262
+ parent.div('.inclusion-radios', container => {
263
+ for (const possible of filter.possible_values || []) {
264
+ container.label('.body-size', label => {
265
+ this.inputFields.checkbox(label, `${filter.input_key}-${possible}`)
266
+ label.span().text(inflection.titleize(possible))
267
+ })
268
+ }
269
+ })
270
+ }
271
+
272
+ }
@@ -0,0 +1,227 @@
1
+ import TerrierPart from "../../terrier/parts/terrier-part"
2
+ import {Logger} from "tuff-core/logging"
3
+ import {messages} from "tuff-core"
4
+ import {PartTag} from "tuff-core/parts"
5
+ import {DdDive, DdDiveEnumFields, UnpersistedDdDive} from "../gen/models"
6
+ import inflection from "inflection"
7
+ import {SchemaDef} from "../../terrier/schema"
8
+ import {ModalPart} from "../../terrier/modals"
9
+ import {Query, QueryModelPicker} from "../queries/queries"
10
+ import Ids from "../../terrier/ids"
11
+ import Db from "../dd-db"
12
+ import {routes} from "../dd-routes"
13
+ import Nav from "tuff-core/nav"
14
+ import {DbErrors} from "../../terrier/db-client"
15
+ import {TerrierFormFields} from "../../terrier/forms"
16
+ import DdSession from "../dd-session"
17
+ import Dives from "./dives";
18
+
19
+ const log = new Logger("DiveForm")
20
+
21
+ export const DiveSettingsColumns = ['name', 'description_raw', 'visibility', 'dd_dive_group_id'] as const
22
+ export type DiveSettingsColumn = typeof DiveSettingsColumns[number]
23
+
24
+ export type DiveSettings = Pick<DdDive, DiveSettingsColumn>
25
+
26
+ /**
27
+ * A form for editing the basic properties of a dive.
28
+ */
29
+ class DiveForm extends TerrierPart<{dive: DiveSettings, session: DdSession}> {
30
+
31
+ fields!: TerrierFormFields<DiveSettings>
32
+
33
+ async init() {
34
+ this.fields = new TerrierFormFields<DiveSettings>(this, this.state.dive)
35
+ this.listen('datachanged', this.fields.dataChangedKey, m => {
36
+ log.info(`Query fields changed`, m.data)
37
+ Object.assign(this.state.dive, m.data)
38
+ this.emitMessage(DiveForm.settingsChangedKey, m.data)
39
+ }, {attach: 'passive'})
40
+ }
41
+
42
+ static readonly settingsChangedKey = messages.typedKey<DiveSettings>()
43
+
44
+
45
+ get parentClasses(): Array<string> {
46
+ return ['tt-flex', 'column', 'gap']
47
+ }
48
+
49
+ render(parent: PartTag): any {
50
+ this.fields.renderErrorBubble(parent)
51
+ parent.div('.tt-flex.gap.collapsible', row => {
52
+ // name
53
+ row.div('.tt-compound-field', field => {
54
+ field.label().text("Name")
55
+ this.fields.textInput(field, 'name')
56
+ })
57
+
58
+ // group
59
+ row.div('.tt-compound-field.shrink', field => {
60
+ field.label().text("Group")
61
+ this.fields.select(field, 'dd_dive_group_id', this.state.session.groupOptions())
62
+ })
63
+ })
64
+
65
+ // description
66
+ parent.div('.tt-compound-field', field => {
67
+ field.label().text("Description")
68
+ this.fields.textInput(field, 'description_raw')
69
+ })
70
+
71
+ // visibility radios
72
+ parent.div('.tt-flex.gap.align-center.padded', row => {
73
+ row.label('.caption-size').text("Visibility:")
74
+ for (const v of DdDiveEnumFields.visibility) {
75
+ row.label('.caption-size', label => {
76
+ this.fields.radio(label, 'visibility', v)
77
+ label.span().text(inflection.titleize(v))
78
+ })
79
+ }
80
+ })
81
+ }
82
+
83
+ /**
84
+ * Sets the errors on the fields and re-renders the form.
85
+ * @param errors
86
+ */
87
+ setErrors(errors: DbErrors<UnpersistedDdDive>) {
88
+ this.fields.errors = errors
89
+ this.dirty()
90
+ }
91
+
92
+ async serialize(): Promise<DiveSettings> {
93
+ return await this.fields.serialize()
94
+ }
95
+ }
96
+
97
+
98
+ ////////////////////////////////////////////////////////////////////////////////
99
+ // Dive Settings Modal
100
+ ////////////////////////////////////////////////////////////////////////////////
101
+
102
+ type DiveSettingsState = {
103
+ schema: SchemaDef
104
+ session: DdSession
105
+ dive: UnpersistedDdDive
106
+ }
107
+
108
+ export class DiveSettingsModal extends ModalPart<DiveSettingsState> {
109
+
110
+ settingsForm!: DiveForm
111
+ modelPicker!: QueryModelPicker
112
+ saveKey = messages.untypedKey()
113
+ deleteKey = messages.untypedKey()
114
+ isNew = true
115
+
116
+ async init() {
117
+ this.isNew = !this.state.dive.id?.length
118
+
119
+ this.settingsForm = this.makePart(DiveForm, {
120
+ dive: this.state.dive, session: this.state.session
121
+ })
122
+ this.modelPicker = this.makePart(QueryModelPicker, {schema: this.state.schema})
123
+
124
+ if (this.isNew) {
125
+ this.setTitle("New Dive")
126
+ }
127
+ else {
128
+ this.setTitle("Edit Dive")
129
+ }
130
+ this.setIcon('glyp-data_dive')
131
+
132
+ this.addAction({
133
+ title: "Save",
134
+ icon: 'glyp-checkmark',
135
+ click: {key: this.saveKey}
136
+ })
137
+
138
+ if (!this.isNew && Dives.canDelete(this.state.dive, this.state.session)) {
139
+ this.addAction({
140
+ title: 'Delete',
141
+ icon: 'glyp-delete',
142
+ classes: ['alert'],
143
+ click: {key: this.deleteKey}
144
+ }, 'secondary')
145
+ }
146
+
147
+ this.onClick(this.saveKey, async _ => {
148
+ await this.save()
149
+ })
150
+
151
+ this.onClick(this.deleteKey, async _ => {
152
+ this.app.confirm({title: 'Delete this dive?', body: "Are you sure you want to delete this dive?", icon: 'glyp-delete'}, () => {
153
+ this.delete()
154
+ })
155
+ })
156
+ }
157
+
158
+ renderContent(parent: PartTag): void {
159
+ parent.div('.tt-flex.tt-form.padded.column.gap.dd-new-dive-form', col => {
160
+ col.part(this.settingsForm)
161
+ if (this.isNew) {
162
+ col.part(this.modelPicker)
163
+ }
164
+ })
165
+ }
166
+
167
+ async save() {
168
+ const dive = {...this.state.dive}
169
+
170
+ // settings
171
+ const settings = await this.settingsForm.serialize()
172
+ Object.assign(dive, settings)
173
+
174
+ // model (for new dives only)
175
+ if (this.isNew) {
176
+ const model = this.modelPicker.model
177
+ if (!model) {
178
+ this.showToast("Please select a model", {color: 'alert'})
179
+ return
180
+ }
181
+ const query: Query = {
182
+ id: Ids.makeUuid(),
183
+ name: settings.name,
184
+ notes: '',
185
+ from: {model: model.name}
186
+ }
187
+ dive.query_data = {queries: [query]}
188
+ log.info(`Creating new dive ${dive.name}!`, dive)
189
+ } else {
190
+ log.info(`Updating existing dive ${dive.name}`, dive)
191
+ }
192
+
193
+ // upsert
194
+ const res = await Db().upsert('dd_dive', dive)
195
+ log.info(`Dive upsert response ${res.status}`, res)
196
+ if (res.status == 'success') {
197
+ this.pop()
198
+
199
+ // if it's a new dive, go straight to the editor
200
+ if (this.isNew) {
201
+ this.app.successToast(`Created Dive ${dive.name}`)
202
+ Nav.visit(routes.editor.path({id: res.record!.id}))
203
+ } else {
204
+ // otherwise just reload the list
205
+ this.app.successToast(`Updated Dive ${dive.name}`)
206
+ Nav.visit(routes.list.path({}))
207
+ }
208
+ } else {
209
+ this.settingsForm.setErrors(res.errors)
210
+ }
211
+ }
212
+
213
+ async delete() {
214
+ const dive = {...this.state.dive}
215
+ dive._state = 2
216
+ const res = await Db().upsert('dd_dive', dive)
217
+ if (res.status == 'success') {
218
+ this.pop()
219
+ this.successToast(`Deleted dive ${dive.name}`)
220
+ Nav.visit(routes.list.path({}))
221
+ } else {
222
+ this.settingsForm.setErrors(res.errors)
223
+ }
224
+ }
225
+
226
+ }
227
+
@@ -0,0 +1,81 @@
1
+ import Api from "../../terrier/api"
2
+ import {DdDive, UnpersistedDdDive} from "../gen/models"
3
+ import Db from "../dd-db"
4
+ import DdSession from "../dd-session"
5
+ import {FilterInput} from "../queries/filters"
6
+ import Tables from "../queries/tables"
7
+ import {SchemaDef} from "../../terrier/schema";
8
+
9
+
10
+ ////////////////////////////////////////////////////////////////////////////////
11
+ // Permissions
12
+ ////////////////////////////////////////////////////////////////////////////////
13
+
14
+ /**
15
+ * Only supers can delete other peoples' dives.
16
+ * @param dive
17
+ * @param session
18
+ */
19
+ function canDelete(dive: UnpersistedDdDive, session: DdSession): boolean {
20
+ return dive.owner_id == session.user.id || session.isSuper
21
+ }
22
+
23
+
24
+ ////////////////////////////////////////////////////////////////////////////////
25
+ // Endpoints
26
+ ////////////////////////////////////////////////////////////////////////////////
27
+
28
+ /**
29
+ * Get a dive by id.
30
+ * @param id can be 'test' to get the test dive
31
+ */
32
+ async function get(id: string): Promise<DdDive> {
33
+ if (id == 'test') {
34
+ const res = await Api.safeGet<{ dive: DdDive }>(`/data_dive/test_dive.json`, {})
35
+ return res.dive
36
+ } else {
37
+ return Db().find("dd_dive", id, {owner: {}})
38
+ }
39
+ }
40
+
41
+
42
+ export type DiveListResult = {
43
+ dives: DdDive[]
44
+ }
45
+
46
+ async function list(): Promise<DiveListResult> {
47
+ return await Api.safeGet(`/data_dive/list.json`, {})
48
+ }
49
+
50
+
51
+ ////////////////////////////////////////////////////////////////////////////////
52
+ // Inputs
53
+ ////////////////////////////////////////////////////////////////////////////////
54
+
55
+ /**
56
+ * Flatten all the filters from all the queries into a single array.
57
+ * This approach introduces some limitations but I think it will yield the
58
+ * desired result most of the time.
59
+ * @param dive
60
+ */
61
+ function computeFilterInputs(schema: SchemaDef, dive: DdDive): FilterInput[] {
62
+ const filters: Record<string, FilterInput> = {}
63
+ for (const query of dive.query_data?.queries || []) {
64
+ Tables.computeFilterInputs(schema, query.from, filters)
65
+ }
66
+ return Object.values(filters)
67
+ }
68
+
69
+
70
+ ////////////////////////////////////////////////////////////////////////////////
71
+ // Export
72
+ ////////////////////////////////////////////////////////////////////////////////
73
+
74
+ const Dives = {
75
+ list,
76
+ get,
77
+ canDelete,
78
+ computeFilterInputs
79
+ }
80
+
81
+ export default Dives