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.
- package/data-dive/dd-app.ts +45 -0
- package/data-dive/dd-db.ts +12 -0
- package/data-dive/dd-routes.ts +10 -0
- package/data-dive/dd-session.ts +45 -0
- package/data-dive/dd-user.ts +12 -0
- package/data-dive/dives/dive-editor.ts +244 -0
- package/data-dive/dives/dive-list.ts +164 -0
- package/data-dive/dives/dive-runs.ts +272 -0
- package/data-dive/dives/dive-settings.ts +227 -0
- package/data-dive/dives/dives.ts +81 -0
- package/data-dive/dives/group-editor.ts +139 -0
- package/data-dive/gen/models.ts +192 -0
- package/data-dive/queries/columns.ts +355 -0
- package/data-dive/queries/dates.ts +286 -0
- package/data-dive/queries/filters.ts +723 -0
- package/data-dive/queries/queries.ts +263 -0
- package/data-dive/queries/query-editor.ts +232 -0
- package/data-dive/queries/query-form.ts +51 -0
- package/data-dive/queries/tables.ts +395 -0
- package/data-dive/queries/test/dates.test.ts +178 -0
- package/package.json +1 -1
- package/{parts → terrier/parts}/panel-part.ts +5 -1
- package/tsconfig.json +21 -0
- /package/{README.md → terrier/README.md} +0 -0
- /package/{api.ts → terrier/api.ts} +0 -0
- /package/{app.ts → terrier/app.ts} +0 -0
- /package/{attachments.ts → terrier/attachments.ts} +0 -0
- /package/{db-client.ts → terrier/db-client.ts} +0 -0
- /package/{dropdowns.ts → terrier/dropdowns.ts} +0 -0
- /package/{format.ts → terrier/format.ts} +0 -0
- /package/{forms.ts → terrier/forms.ts} +0 -0
- /package/{fragments.ts → terrier/fragments.ts} +0 -0
- /package/{gen → terrier/gen}/hub-icons.ts +0 -0
- /package/{glyps.ts → terrier/glyps.ts} +0 -0
- /package/{ids.ts → terrier/ids.ts} +0 -0
- /package/{images → terrier/images}/icons/active.svg +0 -0
- /package/{images → terrier/images}/icons/admin.svg +0 -0
- /package/{images → terrier/images}/icons/archive.svg +0 -0
- /package/{images → terrier/images}/icons/arrow_down.svg +0 -0
- /package/{images → terrier/images}/icons/arrow_left.svg +0 -0
- /package/{images → terrier/images}/icons/arrow_right.svg +0 -0
- /package/{images → terrier/images}/icons/arrow_up.svg +0 -0
- /package/{images → terrier/images}/icons/assign.svg +0 -0
- /package/{images → terrier/images}/icons/attachment.svg +0 -0
- /package/{images → terrier/images}/icons/back.svg +0 -0
- /package/{images → terrier/images}/icons/badge.svg +0 -0
- /package/{images → terrier/images}/icons/board.svg +0 -0
- /package/{images → terrier/images}/icons/branch.svg +0 -0
- /package/{images → terrier/images}/icons/bug.svg +0 -0
- /package/{images → terrier/images}/icons/calculator.svg +0 -0
- /package/{images → terrier/images}/icons/checkmark.svg +0 -0
- /package/{images → terrier/images}/icons/close.svg +0 -0
- /package/{images → terrier/images}/icons/clypboard.svg +0 -0
- /package/{images → terrier/images}/icons/comment.svg +0 -0
- /package/{images → terrier/images}/icons/complete.svg +0 -0
- /package/{images → terrier/images}/icons/dashboard.svg +0 -0
- /package/{images → terrier/images}/icons/data_pull.svg +0 -0
- /package/{images → terrier/images}/icons/data_update.svg +0 -0
- /package/{images → terrier/images}/icons/database.svg +0 -0
- /package/{images → terrier/images}/icons/day.svg +0 -0
- /package/{images → terrier/images}/icons/delete.svg +0 -0
- /package/{images → terrier/images}/icons/documentation.svg +0 -0
- /package/{images → terrier/images}/icons/edit.svg +0 -0
- /package/{images → terrier/images}/icons/feature.svg +0 -0
- /package/{images → terrier/images}/icons/flex.svg +0 -0
- /package/{images → terrier/images}/icons/forward.svg +0 -0
- /package/{images → terrier/images}/icons/github.svg +0 -0
- /package/{images → terrier/images}/icons/history.svg +0 -0
- /package/{images → terrier/images}/icons/home.svg +0 -0
- /package/{images → terrier/images}/icons/image.svg +0 -0
- /package/{images → terrier/images}/icons/inbox.svg +0 -0
- /package/{images → terrier/images}/icons/info.svg +0 -0
- /package/{images → terrier/images}/icons/issue.svg +0 -0
- /package/{images → terrier/images}/icons/lane.svg +0 -0
- /package/{images → terrier/images}/icons/lane_asap.svg +0 -0
- /package/{images → terrier/images}/icons/lane_days.svg +0 -0
- /package/{images → terrier/images}/icons/lane_hours.svg +0 -0
- /package/{images → terrier/images}/icons/lane_weeks.svg +0 -0
- /package/{images → terrier/images}/icons/lanes_board.svg +0 -0
- /package/{images → terrier/images}/icons/level_complete.svg +0 -0
- /package/{images → terrier/images}/icons/level_highway.svg +0 -0
- /package/{images → terrier/images}/icons/level_on_ramp.svg +0 -0
- /package/{images → terrier/images}/icons/level_parking.svg +0 -0
- /package/{images → terrier/images}/icons/minus.svg +0 -0
- /package/{images → terrier/images}/icons/night.svg +0 -0
- /package/{images → terrier/images}/icons/origin.svg +0 -0
- /package/{images → terrier/images}/icons/pending.svg +0 -0
- /package/{images → terrier/images}/icons/plus.svg +0 -0
- /package/{images → terrier/images}/icons/post.svg +0 -0
- /package/{images → terrier/images}/icons/pr_closed.svg +0 -0
- /package/{images → terrier/images}/icons/pr_merged.svg +0 -0
- /package/{images → terrier/images}/icons/pr_open.svg +0 -0
- /package/{images → terrier/images}/icons/prioritized.svg +0 -0
- /package/{images → terrier/images}/icons/project.svg +0 -0
- /package/{images → terrier/images}/icons/question.svg +0 -0
- /package/{images → terrier/images}/icons/reaction.svg +0 -0
- /package/{images → terrier/images}/icons/recent.svg +0 -0
- /package/{images → terrier/images}/icons/refresh.svg +0 -0
- /package/{images → terrier/images}/icons/request.svg +0 -0
- /package/{images → terrier/images}/icons/settings.svg +0 -0
- /package/{images → terrier/images}/icons/status.svg +0 -0
- /package/{images → terrier/images}/icons/step_deploy.svg +0 -0
- /package/{images → terrier/images}/icons/step_develop.svg +0 -0
- /package/{images → terrier/images}/icons/step_investigate.svg +0 -0
- /package/{images → terrier/images}/icons/step_review.svg +0 -0
- /package/{images → terrier/images}/icons/step_test.svg +0 -0
- /package/{images → terrier/images}/icons/steps.svg +0 -0
- /package/{images → terrier/images}/icons/steps_board.svg +0 -0
- /package/{images → terrier/images}/icons/subscribe.svg +0 -0
- /package/{images → terrier/images}/icons/support.svg +0 -0
- /package/{images → terrier/images}/icons/terrier.svg +0 -0
- /package/{images → terrier/images}/icons/thumbs_up.svg +0 -0
- /package/{images → terrier/images}/icons/type.svg +0 -0
- /package/{images → terrier/images}/icons/unprioritized.svg +0 -0
- /package/{images → terrier/images}/icons/upload.svg +0 -0
- /package/{images → terrier/images}/icons/user.svg +0 -0
- /package/{images → terrier/images}/icons/users.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-active.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-admin.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-archive.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-arrow_down.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-arrow_left.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-arrow_right.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-arrow_up.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-assign.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-attachment.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-back.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-badge.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-board.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-branch.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-bug.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-calculator.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-checkmark.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-close.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-clypboard.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-comment.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-complete.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-dashboard.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-data_pull.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-data_update.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-database.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-day.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-delete.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-documentation.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-edit.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-feature.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-flex.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-forward.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-github.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-history.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-home.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-image.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-inbox.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-info.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-issue.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-lane.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-lane_asap.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-lane_days.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-lane_hours.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-lane_weeks.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-lanes_board.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-level_complete.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-level_highway.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-level_on_ramp.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-level_parking.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-minus.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-night.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-origin.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-pending.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-plus.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-post.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-pr_closed.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-pr_merged.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-pr_open.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-prioritized.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-project.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-question.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-reaction.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-recent.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-refresh.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-request.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-settings.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-status.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-step_deploy.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-step_develop.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-step_investigate.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-step_review.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-step_test.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-steps.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-steps_board.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-subscribe.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-support.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-terrier.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-thumbs_up.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-type.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-unprioritized.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-upload.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-user.svg +0 -0
- /package/{images → terrier/images}/optimized/icon-users.svg +0 -0
- /package/{images → terrier/images}/optimized/terrier-hub-favicon.svg +0 -0
- /package/{images → terrier/images}/optimized/terrier-hub-icon-dark.svg +0 -0
- /package/{images → terrier/images}/optimized/terrier-hub-icon-light.svg +0 -0
- /package/{images → terrier/images}/optimized/terrier-hub-loader.svg +0 -0
- /package/{images → terrier/images}/optimized/terrier-hub-logo-dark.svg +0 -0
- /package/{images → terrier/images}/optimized/terrier-hub-logo-light.svg +0 -0
- /package/{images → terrier/images}/raw/icon-active.svg +0 -0
- /package/{images → terrier/images}/raw/icon-admin.svg +0 -0
- /package/{images → terrier/images}/raw/icon-archive.svg +0 -0
- /package/{images → terrier/images}/raw/icon-arrow_down.svg +0 -0
- /package/{images → terrier/images}/raw/icon-arrow_left.svg +0 -0
- /package/{images → terrier/images}/raw/icon-arrow_right.svg +0 -0
- /package/{images → terrier/images}/raw/icon-arrow_up.svg +0 -0
- /package/{images → terrier/images}/raw/icon-assign.svg +0 -0
- /package/{images → terrier/images}/raw/icon-attachment.svg +0 -0
- /package/{images → terrier/images}/raw/icon-back.svg +0 -0
- /package/{images → terrier/images}/raw/icon-badge.svg +0 -0
- /package/{images → terrier/images}/raw/icon-board.svg +0 -0
- /package/{images → terrier/images}/raw/icon-branch.svg +0 -0
- /package/{images → terrier/images}/raw/icon-bug.svg +0 -0
- /package/{images → terrier/images}/raw/icon-calculator.svg +0 -0
- /package/{images → terrier/images}/raw/icon-checkmark.svg +0 -0
- /package/{images → terrier/images}/raw/icon-close.svg +0 -0
- /package/{images → terrier/images}/raw/icon-clypboard.svg +0 -0
- /package/{images → terrier/images}/raw/icon-comment.svg +0 -0
- /package/{images → terrier/images}/raw/icon-complete.svg +0 -0
- /package/{images → terrier/images}/raw/icon-dashboard.svg +0 -0
- /package/{images → terrier/images}/raw/icon-data_pull.svg +0 -0
- /package/{images → terrier/images}/raw/icon-data_update.svg +0 -0
- /package/{images → terrier/images}/raw/icon-database.svg +0 -0
- /package/{images → terrier/images}/raw/icon-day.svg +0 -0
- /package/{images → terrier/images}/raw/icon-delete.svg +0 -0
- /package/{images → terrier/images}/raw/icon-documentation.svg +0 -0
- /package/{images → terrier/images}/raw/icon-edit.svg +0 -0
- /package/{images → terrier/images}/raw/icon-feature.svg +0 -0
- /package/{images → terrier/images}/raw/icon-flex.svg +0 -0
- /package/{images → terrier/images}/raw/icon-forward.svg +0 -0
- /package/{images → terrier/images}/raw/icon-github.svg +0 -0
- /package/{images → terrier/images}/raw/icon-history.svg +0 -0
- /package/{images → terrier/images}/raw/icon-home.svg +0 -0
- /package/{images → terrier/images}/raw/icon-image.svg +0 -0
- /package/{images → terrier/images}/raw/icon-inbox.svg +0 -0
- /package/{images → terrier/images}/raw/icon-info.svg +0 -0
- /package/{images → terrier/images}/raw/icon-issue.svg +0 -0
- /package/{images → terrier/images}/raw/icon-lane.svg +0 -0
- /package/{images → terrier/images}/raw/icon-lane_asap.svg +0 -0
- /package/{images → terrier/images}/raw/icon-lane_days.svg +0 -0
- /package/{images → terrier/images}/raw/icon-lane_hours.svg +0 -0
- /package/{images → terrier/images}/raw/icon-lane_weeks.svg +0 -0
- /package/{images → terrier/images}/raw/icon-lanes_board.svg +0 -0
- /package/{images → terrier/images}/raw/icon-level_complete.svg +0 -0
- /package/{images → terrier/images}/raw/icon-level_highway.svg +0 -0
- /package/{images → terrier/images}/raw/icon-level_on_ramp.svg +0 -0
- /package/{images → terrier/images}/raw/icon-level_parking.svg +0 -0
- /package/{images → terrier/images}/raw/icon-minus.svg +0 -0
- /package/{images → terrier/images}/raw/icon-night.svg +0 -0
- /package/{images → terrier/images}/raw/icon-origin.svg +0 -0
- /package/{images → terrier/images}/raw/icon-pending.svg +0 -0
- /package/{images → terrier/images}/raw/icon-plus.svg +0 -0
- /package/{images → terrier/images}/raw/icon-post.svg +0 -0
- /package/{images → terrier/images}/raw/icon-pr_closed.svg +0 -0
- /package/{images → terrier/images}/raw/icon-pr_merged.svg +0 -0
- /package/{images → terrier/images}/raw/icon-pr_open.svg +0 -0
- /package/{images → terrier/images}/raw/icon-prioritized.svg +0 -0
- /package/{images → terrier/images}/raw/icon-project.svg +0 -0
- /package/{images → terrier/images}/raw/icon-question.svg +0 -0
- /package/{images → terrier/images}/raw/icon-reaction.svg +0 -0
- /package/{images → terrier/images}/raw/icon-recent.svg +0 -0
- /package/{images → terrier/images}/raw/icon-refresh.svg +0 -0
- /package/{images → terrier/images}/raw/icon-request.svg +0 -0
- /package/{images → terrier/images}/raw/icon-settings.svg +0 -0
- /package/{images → terrier/images}/raw/icon-status.svg +0 -0
- /package/{images → terrier/images}/raw/icon-step_deploy.svg +0 -0
- /package/{images → terrier/images}/raw/icon-step_develop.svg +0 -0
- /package/{images → terrier/images}/raw/icon-step_investigate.svg +0 -0
- /package/{images → terrier/images}/raw/icon-step_review.svg +0 -0
- /package/{images → terrier/images}/raw/icon-step_test.svg +0 -0
- /package/{images → terrier/images}/raw/icon-steps.svg +0 -0
- /package/{images → terrier/images}/raw/icon-steps_board.svg +0 -0
- /package/{images → terrier/images}/raw/icon-subscribe.svg +0 -0
- /package/{images → terrier/images}/raw/icon-support.svg +0 -0
- /package/{images → terrier/images}/raw/icon-terrier.svg +0 -0
- /package/{images → terrier/images}/raw/icon-thumbs_up.svg +0 -0
- /package/{images → terrier/images}/raw/icon-type.svg +0 -0
- /package/{images → terrier/images}/raw/icon-unprioritized.svg +0 -0
- /package/{images → terrier/images}/raw/icon-upload.svg +0 -0
- /package/{images → terrier/images}/raw/icon-user.svg +0 -0
- /package/{images → terrier/images}/raw/icon-users.svg +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-favicon-alert.png +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-favicon-dark.png +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-favicon.png +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-favicon.svg +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-icon-dark.svg +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-icon-light.png +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-icon-light.svg +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-loader.svg +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-logo-dark.svg +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-logo-light.png +0 -0
- /package/{images → terrier/images}/raw/terrier-hub-logo-light.svg +0 -0
- /package/{lightbox.ts → terrier/lightbox.ts} +0 -0
- /package/{loading.ts → terrier/loading.ts} +0 -0
- /package/{modals.ts → terrier/modals.ts} +0 -0
- /package/{overlays.ts → terrier/overlays.ts} +0 -0
- /package/{parts → terrier/parts}/content-part.ts +0 -0
- /package/{parts → terrier/parts}/not-found-page.ts +0 -0
- /package/{parts → terrier/parts}/page-part.ts +0 -0
- /package/{parts → terrier/parts}/terrier-form-part.ts +0 -0
- /package/{parts → terrier/parts}/terrier-part.ts +0 -0
- /package/{schema.ts → terrier/schema.ts} +0 -0
- /package/{sheets.ts → terrier/sheets.ts} +0 -0
- /package/{tabs.ts → terrier/tabs.ts} +0 -0
- /package/{theme.ts → terrier/theme.ts} +0 -0
- /package/{toasts.ts → terrier/toasts.ts} +0 -0
- /package/{tooltips.ts → terrier/tooltips.ts} +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {NoState, PartTag} from "tuff-core/parts"
|
|
2
|
+
import {TerrierApp} from "../terrier/app"
|
|
3
|
+
import {RouterPart} from "tuff-core/routing"
|
|
4
|
+
import {routes} from "./dd-routes"
|
|
5
|
+
import {DiveListPage} from "./dives/dive-list"
|
|
6
|
+
import {Logger} from "tuff-core/logging"
|
|
7
|
+
import DdSession from "./dd-session"
|
|
8
|
+
|
|
9
|
+
Logger.level = 'debug'
|
|
10
|
+
|
|
11
|
+
class ContentRouterPart extends RouterPart {
|
|
12
|
+
get defaultPart() {
|
|
13
|
+
return DiveListPage
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get routes() {
|
|
17
|
+
return routes
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default class DdApp extends TerrierApp<NoState> {
|
|
23
|
+
|
|
24
|
+
contentPart!: ContentRouterPart
|
|
25
|
+
session!: DdSession
|
|
26
|
+
|
|
27
|
+
async init() {
|
|
28
|
+
await super.init()
|
|
29
|
+
this.contentPart = this.makePart(ContentRouterPart, {})
|
|
30
|
+
|
|
31
|
+
this.session = await DdSession.get()
|
|
32
|
+
|
|
33
|
+
this.dirty()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get parentClasses(): Array<string> {
|
|
37
|
+
return ['tt-typography']
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
render(parent: PartTag) {
|
|
41
|
+
parent.part(this.contentPart)
|
|
42
|
+
parent.part(this.overlayPart)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import DbClient from "../terrier/db-client"
|
|
2
|
+
import {ModelIncludesMap, PersistedModelTypeMap, UnpersistedModelTypeMap} from "./gen/models"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* DbClient specifically for the data-dive database.
|
|
6
|
+
*/
|
|
7
|
+
class DdDb extends DbClient<PersistedModelTypeMap, UnpersistedModelTypeMap, ModelIncludesMap> {
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function Db() {
|
|
11
|
+
return new DdDb()
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import {partRoute} from "tuff-core/routing"
|
|
2
|
+
import {stringParser} from 'typesafe-routes'
|
|
3
|
+
import {DiveListPage} from "./dives/dive-list"
|
|
4
|
+
import {DiveEditorPage} from "./dives/dive-editor"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export const routes = {
|
|
8
|
+
list: partRoute(DiveListPage, '/data_dive', {}),
|
|
9
|
+
editor: partRoute(DiveEditorPage, '/data_dive/editor/:id', {id: stringParser})
|
|
10
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {DdUser} from "./dd-user"
|
|
2
|
+
import Api from "../terrier/api"
|
|
3
|
+
import {DdDiveGroup} from "./gen/models"
|
|
4
|
+
import {arrays} from "tuff-core";
|
|
5
|
+
import {SelectOptions} from "tuff-core/forms"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
type DdSessionData = {
|
|
9
|
+
user: DdUser
|
|
10
|
+
groupMap: Record<string, DdDiveGroup>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Stores authentication information as well as various options from the server.
|
|
15
|
+
*/
|
|
16
|
+
export default class DdSession {
|
|
17
|
+
|
|
18
|
+
constructor(readonly data: DdSessionData) {
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get user(): DdUser {
|
|
22
|
+
return this.data.user
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get isSuper(): boolean {
|
|
26
|
+
return this.user.role == 'super'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
groupsInOrder(): DdDiveGroup[] {
|
|
30
|
+
return arrays.sortBy(Object.values(this.data.groupMap), 'name')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
groupOptions(): SelectOptions {
|
|
34
|
+
return this.groupsInOrder().map(g => {
|
|
35
|
+
return {title: g.name, value: g.id}
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static async get(): Promise<DdSession> {
|
|
40
|
+
const data = await Api.safeGet<DdSessionData>("/data_dive/user_session.json", {})
|
|
41
|
+
return new DdSession(data)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import Schema, {SchemaDef} from "../../terrier/schema"
|
|
2
|
+
import {PartTag} from "tuff-core/parts"
|
|
3
|
+
import Dives from "./dives"
|
|
4
|
+
import {Query, QueryModelPicker} from "../queries/queries"
|
|
5
|
+
import QueryEditor from "../queries/query-editor"
|
|
6
|
+
import {Logger} from "tuff-core/logging"
|
|
7
|
+
import QueryForm from "../queries/query-form"
|
|
8
|
+
import {arrays, messages} from "tuff-core"
|
|
9
|
+
import {TabContainerPart} from "../../terrier/tabs"
|
|
10
|
+
import PagePart from "../../terrier/parts/page-part"
|
|
11
|
+
import ContentPart from "../../terrier/parts/content-part"
|
|
12
|
+
import {ModalPart} from "../../terrier/modals"
|
|
13
|
+
import {DdDive} from "../gen/models"
|
|
14
|
+
import Ids from "../../terrier/ids"
|
|
15
|
+
import Db from "../dd-db"
|
|
16
|
+
import DdSession from "../dd-session"
|
|
17
|
+
import {DiveRunModal} from "./dive-runs";
|
|
18
|
+
|
|
19
|
+
const log = new Logger("DiveEditor")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
23
|
+
// Editor
|
|
24
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
25
|
+
|
|
26
|
+
export type DiveEditorState = {
|
|
27
|
+
schema: SchemaDef
|
|
28
|
+
dive: DdDive
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export default class DiveEditor extends ContentPart<DiveEditorState> {
|
|
32
|
+
|
|
33
|
+
tabs!: TabContainerPart
|
|
34
|
+
|
|
35
|
+
newQueryKey = messages.untypedKey()
|
|
36
|
+
|
|
37
|
+
queries = new Array<Query>()
|
|
38
|
+
|
|
39
|
+
async init() {
|
|
40
|
+
this.tabs = this.makePart(TabContainerPart, {side: 'top'})
|
|
41
|
+
|
|
42
|
+
this.tabs.setBeforeAction({
|
|
43
|
+
title: 'Queries:',
|
|
44
|
+
icon: 'glyp-data_dive_query'
|
|
45
|
+
})
|
|
46
|
+
this.tabs.setAfterAction({
|
|
47
|
+
icon: 'glyp-plus_outline',
|
|
48
|
+
click: {key: this.newQueryKey}
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
this.queries = this.state.dive.query_data?.queries || []
|
|
52
|
+
for (const query of this.queries) {
|
|
53
|
+
this.addQuery(query)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
this.listenMessage(QueryForm.settingsChangedKey, m => {
|
|
57
|
+
const query = m.data
|
|
58
|
+
log.info(`Query settings changed`, query)
|
|
59
|
+
this.tabs.updateTab({key: query.id, title: query.name})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
this.onClick(this.newQueryKey, _ => {
|
|
63
|
+
this.app.showModal(NewQueryModal, {editor: this as DiveEditor, schema: this.state.schema})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
this.onClick(DiveEditor.deleteQueryKey, m => {
|
|
67
|
+
this.app.confirm({title: "Delete Query", icon: 'glyp-delete', body: "Are you sure you want to delete this query?"}, () => {
|
|
68
|
+
this.deleteQuery(m.data.id)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
addQuery(query: Query) {
|
|
74
|
+
const state = {...this.state, query}
|
|
75
|
+
this.tabs.upsertTab({key: query.id, title: query.name}, QueryEditor, state)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
deleteQuery(id: string) {
|
|
79
|
+
log.info(`Deleting query ${id}`)
|
|
80
|
+
if (arrays.deleteIf(this.queries, q => q.id == id) > 0) {
|
|
81
|
+
this.tabs.removeTab(id)
|
|
82
|
+
this.dirty()
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get parentClasses(): Array<string> {
|
|
87
|
+
return ['dd-dive-editor']
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
renderContent(parent: PartTag) {
|
|
91
|
+
parent.part(this.tabs)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static readonly deleteQueryKey = messages.typedKey<{ id: string }>()
|
|
95
|
+
|
|
96
|
+
async serialize(): Promise<DdDive> {
|
|
97
|
+
const queries = this.queries
|
|
98
|
+
return {...this.state.dive, query_data: {queries}}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
export class DiveEditorPage extends PagePart<{id: string}> {
|
|
105
|
+
|
|
106
|
+
editor!: DiveEditor
|
|
107
|
+
session!: DdSession
|
|
108
|
+
|
|
109
|
+
saveKey = messages.untypedKey()
|
|
110
|
+
runKey = messages.untypedKey()
|
|
111
|
+
|
|
112
|
+
async init() {
|
|
113
|
+
log.info(`Loading dive ${this.state.id}`)
|
|
114
|
+
|
|
115
|
+
const schema = await Schema.get()
|
|
116
|
+
this.session = await DdSession.get()
|
|
117
|
+
const dive = await Dives.get(this.state.id)
|
|
118
|
+
this.editor = this.makePart(DiveEditor, {schema, dive})
|
|
119
|
+
|
|
120
|
+
this.addBreadcrumb({
|
|
121
|
+
title: "Dives",
|
|
122
|
+
icon: 'glyp-data_dives',
|
|
123
|
+
href: "/data_dive"
|
|
124
|
+
})
|
|
125
|
+
this.addBreadcrumb({
|
|
126
|
+
title: dive.name,
|
|
127
|
+
icon: 'glyp-data_dive'
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
this.addAction({
|
|
131
|
+
title: 'Save',
|
|
132
|
+
icon: 'glyp-checkmark',
|
|
133
|
+
click: {key: this.saveKey}
|
|
134
|
+
}, 'tertiary')
|
|
135
|
+
|
|
136
|
+
this.addAction({
|
|
137
|
+
title: 'Run',
|
|
138
|
+
icon: 'glyp-play',
|
|
139
|
+
click: {key: this.runKey}
|
|
140
|
+
}, 'tertiary')
|
|
141
|
+
|
|
142
|
+
this.onClick(this.saveKey, _ => this.save())
|
|
143
|
+
|
|
144
|
+
this.onClick(this.runKey, _ => this.run())
|
|
145
|
+
|
|
146
|
+
this.dirty()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
get parentClasses(): Array<string> {
|
|
150
|
+
return ['dd-dive-editor-page']
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
renderContent(parent: PartTag) {
|
|
154
|
+
parent.part(this.editor)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async save() {
|
|
158
|
+
const dive = await this.editor.serialize()
|
|
159
|
+
log.info(`Saving dive ${dive.name}`, dive)
|
|
160
|
+
const res = await Db().upsert('dd_dive', dive)
|
|
161
|
+
if (res.status == 'success') {
|
|
162
|
+
this.successToast(`Saved Dive!`)
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
this.alertToast(res.message)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async run() {
|
|
170
|
+
const dive = await this.editor.serialize()
|
|
171
|
+
this.app.showModal(DiveRunModal, {dive})
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
177
|
+
// New Query Modal
|
|
178
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
179
|
+
|
|
180
|
+
type NewQueryState = {
|
|
181
|
+
editor: DiveEditor
|
|
182
|
+
schema: SchemaDef
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
class NewQueryModal extends ModalPart<NewQueryState> {
|
|
186
|
+
|
|
187
|
+
addKey = messages.untypedKey()
|
|
188
|
+
settingsForm!: QueryForm
|
|
189
|
+
modelPicker!: QueryModelPicker
|
|
190
|
+
|
|
191
|
+
async init() {
|
|
192
|
+
this.settingsForm = this.makePart(QueryForm, {query: {id: 'new', name: '', notes: ''}})
|
|
193
|
+
this.modelPicker = this.makePart(QueryModelPicker, {schema: this.state.schema})
|
|
194
|
+
|
|
195
|
+
this.setIcon('glyp-data_dive_query')
|
|
196
|
+
this.setTitle("New Query")
|
|
197
|
+
|
|
198
|
+
this.addAction({
|
|
199
|
+
title: "Add",
|
|
200
|
+
icon: 'glyp-plus',
|
|
201
|
+
click: {key: this.addKey}
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
this.onClick(this.addKey, async _ => {
|
|
205
|
+
await this.save()
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
this.onChange(this.modelPicker.pickedKey, m => {
|
|
209
|
+
log.info(`Picked model ${m.data.model}`)
|
|
210
|
+
const model = this.state.schema.models[m.data.model]
|
|
211
|
+
if (!this.settingsForm.state.query.name?.length) {
|
|
212
|
+
this.settingsForm.state.query.name = model.name
|
|
213
|
+
this.settingsForm.dirty()
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
renderContent(parent: PartTag): void {
|
|
219
|
+
parent.div('.tt-flex.tt-form.padded.column.gap.dd-new-query-form', col => {
|
|
220
|
+
col.part(this.settingsForm)
|
|
221
|
+
col.part(this.modelPicker)
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async save() {
|
|
226
|
+
log.info(`Saving new query`)
|
|
227
|
+
const settings = await this.settingsForm.fields.serialize()
|
|
228
|
+
if (!settings.name?.length) {
|
|
229
|
+
this.showToast("Please enter a query name", {color: 'alert'})
|
|
230
|
+
this.dirty()
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
const model = this.modelPicker.model
|
|
234
|
+
if (!model) {
|
|
235
|
+
this.showToast("Please select a model", {color: 'alert'})
|
|
236
|
+
return
|
|
237
|
+
}
|
|
238
|
+
const query = {...settings, id: Ids.makeUuid(), from: {model: model.name}}
|
|
239
|
+
this.state.editor.addQuery(query)
|
|
240
|
+
this.state.editor.tabs.showTab(query.id)
|
|
241
|
+
this.pop()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import {Logger} from "tuff-core/logging"
|
|
2
|
+
import PagePart from "../../terrier/parts/page-part"
|
|
3
|
+
import {PartTag} from "tuff-core/parts"
|
|
4
|
+
import Schema, {SchemaDef} from "../../terrier/schema"
|
|
5
|
+
import {arrays, messages} from "tuff-core"
|
|
6
|
+
import {DdDive, DdDiveGroup, UnpersistedDdDive, UnpersistedDdDiveGroup} from "../gen/models"
|
|
7
|
+
import Dives, {DiveListResult} from "./dives"
|
|
8
|
+
import {GroupEditorModal} from "./group-editor"
|
|
9
|
+
import Fragments from "../../terrier/fragments"
|
|
10
|
+
import {IconName} from "../../terrier/theme"
|
|
11
|
+
import {routes} from "../dd-routes"
|
|
12
|
+
import {DiveSettingsModal} from "./dive-settings"
|
|
13
|
+
import DdSession from "../dd-session"
|
|
14
|
+
|
|
15
|
+
const log = new Logger("DiveList")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
19
|
+
// List
|
|
20
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
21
|
+
|
|
22
|
+
export class DiveListPage extends PagePart<{}> {
|
|
23
|
+
|
|
24
|
+
newGroupKey = messages.untypedKey()
|
|
25
|
+
editGroupKey = messages.typedKey<{id: string}>()
|
|
26
|
+
newDiveKey = messages.typedKey<{group_id: string}>()
|
|
27
|
+
editDiveKey = messages.typedKey<{id: string}>()
|
|
28
|
+
|
|
29
|
+
session!: DdSession
|
|
30
|
+
result!: DiveListResult
|
|
31
|
+
schema!: SchemaDef
|
|
32
|
+
groupMap: Record<string, DdDiveGroup> = {}
|
|
33
|
+
diveMap: Record<string,DdDive> = {}
|
|
34
|
+
|
|
35
|
+
async init() {
|
|
36
|
+
this.setTitle("Data Dive")
|
|
37
|
+
this.setIcon('glyp-data_dives')
|
|
38
|
+
|
|
39
|
+
this.schema = await Schema.get()
|
|
40
|
+
this.session = await DdSession.get()
|
|
41
|
+
|
|
42
|
+
this.addAction({
|
|
43
|
+
title: "New Group",
|
|
44
|
+
icon: 'glyp-plus_outline',
|
|
45
|
+
click: {key: this.newGroupKey}
|
|
46
|
+
}, 'tertiary')
|
|
47
|
+
|
|
48
|
+
this.onClick(this.newGroupKey, _ => {
|
|
49
|
+
log.info("Showing new dive group model")
|
|
50
|
+
const newGroup = {name: '', group_types: []}
|
|
51
|
+
this.app.showModal(GroupEditorModal, {session: this.session,
|
|
52
|
+
group: newGroup,
|
|
53
|
+
callback: _ => this.reload()
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
this.onClick(this.editGroupKey, m => {
|
|
58
|
+
log.info(`Edit group ${m.data.id}`)
|
|
59
|
+
const group = this.groupMap[m.data.id]
|
|
60
|
+
if (group) {
|
|
61
|
+
this.app.showModal(GroupEditorModal, {
|
|
62
|
+
session: this.session,
|
|
63
|
+
group: group as UnpersistedDdDiveGroup,
|
|
64
|
+
callback: _ => this.reload()
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
this.alertToast(`No group with id ${m.data.id}`)
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
this.onClick(this.newDiveKey, m => {
|
|
73
|
+
const groupId = m.data.group_id
|
|
74
|
+
log.info(`Showing new dive modal for group ${groupId}`)
|
|
75
|
+
const dive: UnpersistedDdDive = {
|
|
76
|
+
name: '',
|
|
77
|
+
description_raw: '',
|
|
78
|
+
visibility: 'public',
|
|
79
|
+
owner_id: this.session.user.id,
|
|
80
|
+
dd_dive_group_id: groupId,
|
|
81
|
+
dive_types: []
|
|
82
|
+
}
|
|
83
|
+
this.app.showModal(DiveSettingsModal, {schema: this.schema, dive, session: this.session})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
this.onClick(this.editDiveKey, m => {
|
|
87
|
+
const dive = this.diveMap[m.data.id] as UnpersistedDdDive
|
|
88
|
+
if (dive) {
|
|
89
|
+
log.info(`Editing settings for dive: ${dive.name}`)
|
|
90
|
+
this.app.showModal(DiveSettingsModal, {schema: this.schema, dive, session: this.session})
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
log.warn(`No dive with id ${m.data.id}`)
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
await this.reload()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async reload() {
|
|
101
|
+
this.session = await DdSession.get()
|
|
102
|
+
this.result = await Dives.list()
|
|
103
|
+
log.info("Loading data dive list", this.result)
|
|
104
|
+
this.groupMap = this.session.data.groupMap
|
|
105
|
+
this.diveMap = arrays.indexBy(this.result.dives, 'id')
|
|
106
|
+
this.dirty()
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
renderContent(parent: PartTag): void {
|
|
110
|
+
|
|
111
|
+
const groupedDives = arrays.groupBy(this.result.dives, 'dd_dive_group_id')
|
|
112
|
+
|
|
113
|
+
parent.div('.dd-group-grid', grid => {
|
|
114
|
+
const groups = arrays.sortBy(Object.values(this.groupMap), 'name')
|
|
115
|
+
for (const group of groups) {
|
|
116
|
+
this.renderGroupPanel(grid, group, groupedDives[group.id] || [])
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
renderGroupPanel(parent: PartTag, group: DdDiveGroup, dives: DdDive[]) {
|
|
122
|
+
Fragments.panel(this.theme)
|
|
123
|
+
.title(group.name)
|
|
124
|
+
.icon((group.icon || 'glyp-data_dives') as IconName)
|
|
125
|
+
.classes('group')
|
|
126
|
+
.content(content => {
|
|
127
|
+
content.class('tt-list')
|
|
128
|
+
for (const dive of arrays.sortBy(dives, 'name')) {
|
|
129
|
+
this.renderDiveRow(content, dive)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.addAction({
|
|
133
|
+
title: "New Dive",
|
|
134
|
+
icon: 'glyp-data_dive',
|
|
135
|
+
click: {key: this.newDiveKey, data: {group_id: group.id}}
|
|
136
|
+
}, 'secondary')
|
|
137
|
+
.addAction({
|
|
138
|
+
icon: 'glyp-settings',
|
|
139
|
+
click: {key: this.editGroupKey, data: {id: group.id}}
|
|
140
|
+
}, 'tertiary')
|
|
141
|
+
.render(parent)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
renderDiveRow(parent: PartTag, dive: DdDive) {
|
|
145
|
+
parent.div('.dive', row => {
|
|
146
|
+
row.a({href: routes.editor.path({id: dive.id})}, a => {
|
|
147
|
+
if (dive.visibility == 'private') {
|
|
148
|
+
a.i('.glyp-privacy')
|
|
149
|
+
.data({tooltip: "Private Dive"})
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
a.i('.glyp-data_dive')
|
|
153
|
+
}
|
|
154
|
+
a.span().text(dive.name)
|
|
155
|
+
}).data({tooltip: "Open Editor"})
|
|
156
|
+
row.a('.icon-only', a => {
|
|
157
|
+
a.i('.glyp-settings')
|
|
158
|
+
}).data({tooltip: "Dive Settings"})
|
|
159
|
+
.emitClick(this.editDiveKey, {id: dive.id})
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
}
|
|
164
|
+
|