blazer 3.4.0 → 3.5.0

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/README.md +23 -27
  4. data/app/assets/javascripts/blazer/Sortable.js +826 -1162
  5. data/app/assets/javascripts/blazer/ace/ace.js +15775 -13360
  6. data/app/assets/javascripts/blazer/ace/ext-language_tools.js +1411 -668
  7. data/app/assets/javascripts/blazer/ace/mode-sql.js +12 -6
  8. data/app/assets/javascripts/blazer/ace/theme-twilight.js +3 -3
  9. data/app/assets/javascripts/blazer/application.js +94 -52
  10. data/app/assets/javascripts/blazer/chart.umd.js +6 -6
  11. data/app/assets/javascripts/blazer/daterangepicker.js +193 -199
  12. data/app/assets/javascripts/blazer/highlight.min.js +235 -373
  13. data/app/assets/javascripts/blazer/moment-timezone-with-data.js +116 -82
  14. data/app/assets/javascripts/blazer/moment.js +368 -365
  15. data/app/assets/javascripts/blazer/queries.js +68 -58
  16. data/app/assets/javascripts/blazer/routes.js +11 -11
  17. data/app/assets/javascripts/blazer/tablesort.js +42 -0
  18. data/app/assets/javascripts/blazer/tom-select.base.js +4175 -0
  19. data/app/assets/stylesheets/blazer/application.css +114 -5
  20. data/app/assets/stylesheets/blazer/tom-select.css +502 -0
  21. data/app/controllers/blazer/base_controller.rb +1 -1
  22. data/app/controllers/blazer/checks_controller.rb +5 -2
  23. data/app/controllers/blazer/queries_controller.rb +3 -1
  24. data/app/helpers/blazer/base_helper.rb +1 -1
  25. data/app/models/blazer/check.rb +35 -57
  26. data/app/views/blazer/_nav.html.erb +1 -1
  27. data/app/views/blazer/_variables.html.erb +57 -42
  28. data/app/views/blazer/checks/_form.html.erb +20 -16
  29. data/app/views/blazer/checks/index.html.erb +17 -20
  30. data/app/views/blazer/dashboards/_form.html.erb +63 -48
  31. data/app/views/blazer/dashboards/show.html.erb +23 -16
  32. data/app/views/blazer/queries/_caching.html.erb +1 -1
  33. data/app/views/blazer/queries/_chart.html.erb +8 -0
  34. data/app/views/blazer/queries/_form.html.erb +153 -180
  35. data/app/views/blazer/queries/_map.html.erb +8 -0
  36. data/app/views/blazer/queries/docs.html.erb +6 -6
  37. data/app/views/blazer/queries/edit.html.erb +1 -0
  38. data/app/views/blazer/queries/home.html.erb +101 -102
  39. data/app/views/blazer/queries/new.html.erb +1 -0
  40. data/app/views/blazer/queries/run.html.erb +23 -34
  41. data/app/views/blazer/queries/schema.html.erb +20 -20
  42. data/app/views/blazer/queries/show.html.erb +19 -12
  43. data/app/views/blazer/uploads/index.html.erb +10 -10
  44. data/app/views/layouts/blazer/application.html.erb +3 -3
  45. data/lib/blazer/adapters/athena_adapter.rb +12 -6
  46. data/lib/blazer/adapters/clickhouse_adapter.rb +136 -0
  47. data/lib/blazer/adapters/druid_adapter.rb +0 -2
  48. data/lib/blazer/adapters/snowflake2_adapter.rb +198 -0
  49. data/lib/blazer/adapters/soda_adapter.rb +0 -2
  50. data/lib/blazer/adapters/sql_adapter.rb +4 -0
  51. data/lib/blazer/adapters.rb +2 -0
  52. data/lib/blazer/check_types.rb +20 -0
  53. data/lib/blazer/email_notifier.rb +49 -0
  54. data/lib/blazer/result.rb +15 -0
  55. data/lib/blazer/slack_notifier.rb +39 -7
  56. data/lib/blazer/version.rb +1 -1
  57. data/lib/blazer.rb +32 -26
  58. data/lib/generators/blazer/templates/config.yml.tt +0 -2
  59. data/licenses/LICENSE-chart.js.txt +1 -1
  60. metadata +12 -31
  61. data/app/assets/javascripts/blazer/bootstrap.js +0 -2580
  62. data/app/assets/javascripts/blazer/jquery.js +0 -10872
  63. data/app/assets/javascripts/blazer/jquery.stickytableheaders.js +0 -325
  64. data/app/assets/javascripts/blazer/rails-ujs.js +0 -746
  65. data/app/assets/javascripts/blazer/selectize.js +0 -3891
  66. data/app/assets/javascripts/blazer/stupidtable-custom-settings.js +0 -13
  67. data/app/assets/javascripts/blazer/stupidtable.js +0 -281
  68. data/app/assets/javascripts/blazer/vue.global.prod.js +0 -1
  69. data/app/assets/stylesheets/blazer/selectize.css +0 -403
  70. data/licenses/LICENSE-jquery.txt +0 -20
  71. data/licenses/LICENSE-rails-ujs.txt +0 -20
  72. data/licenses/LICENSE-stickytableheaders.txt +0 -20
  73. data/licenses/LICENSE-stupidtable.txt +0 -19
  74. data/licenses/LICENSE-vue.txt +0 -21
  75. /data/licenses/{LICENSE-selectize.txt → LICENSE-tom-select.txt} +0 -0
@@ -2,10 +2,10 @@
2
2
 
3
3
  <div class="topbar">
4
4
  <div class="container">
5
- <div class="row" style="padding-top: 13px;">
5
+ <div class="row name-row">
6
6
  <div class="col-sm-9">
7
7
  <%= render partial: "blazer/nav" %>
8
- <h3 style="line-height: 34px; display: inline; margin-left: 5px;">
8
+ <h3 class="name-header">
9
9
  <%= @dashboard.name %>
10
10
  </h3>
11
11
  </div>
@@ -16,10 +16,10 @@
16
16
  </div>
17
17
  </div>
18
18
 
19
- <div style="margin-bottom: 60px;"></div>
19
+ <div class="topbar-margin"></div>
20
20
 
21
21
  <% if @data_sources.any? { |ds| ds.cache_mode != "off" } %>
22
- <p class="text-muted" style="float: right;">
22
+ <p class="text-muted pull-right">
23
23
  Some queries may be cached
24
24
  <%= link_to "Refresh", refresh_dashboard_path(@dashboard, params: variable_params(@dashboard)), method: :post %>
25
25
  </p>
@@ -34,20 +34,27 @@
34
34
  <% @queries.each_with_index do |query, i| %>
35
35
  <div class="chart-container">
36
36
  <h4><%= link_to query.friendly_name, query_path(query, params: variable_params(query)), target: "_blank" %></h4>
37
- <div id="chart-<%= i %>" class="chart">
37
+ <%= tag.div id: "chart-#{i}", class: "chart" do %>
38
38
  <p class="text-muted">Loading...</p>
39
- </div>
39
+ <% end %>
40
40
  </div>
41
+ <% run_data = {statement: query.statement, query_id: query.id, data_source: query.data_source, variables: variable_params(query), only_chart: true} %>
42
+ <% run_data.merge!(cohort_period: params[:cohort_period]) if params[:cohort_period] %>
41
43
  <%= javascript_tag nonce: true do %>
42
- <% data = {statement: query.statement, query_id: query.id, data_source: query.data_source, variables: variable_params(query), only_chart: true} %>
43
- <% data.merge!(cohort_period: params[:cohort_period]) if params[:cohort_period] %>
44
- <%= blazer_js_var "data", data %>
45
-
46
- runQuery(data, function (data) {
47
- $("#chart-<%= i %>").html(data)
48
- $("#chart-<%= i %> table").stupidtable(stupidtableCustomSettings)
49
- }, function (message) {
50
- $("#chart-<%= i %>").addClass("query-error").html(message)
51
- });
44
+ (function () {
45
+ <%= blazer_js_var "chartId", "chart-#{i}" %>
46
+ <%= blazer_js_var "runData", run_data %>
47
+ const chart = document.getElementById(chartId)
48
+ runQuery(runData, function (data) {
49
+ renderResults(chart, data)
50
+ const table = chart.querySelector("table")
51
+ if (table) {
52
+ tablesort(table)
53
+ }
54
+ }, function (message) {
55
+ chart.classList.add("query-error")
56
+ chart.textContent = message
57
+ })
58
+ })()
52
59
  <% end %>
53
60
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <% if @cached_at || @just_cached %>
2
- <p class="text-muted" style="float: right;">
2
+ <p class="text-muted pull-right">
3
3
  <% if @cached_at %>
4
4
  Cached <%= time_ago_in_words(@cached_at, include_seconds: true) %> ago
5
5
  <% elsif params[:query_id] %>
@@ -0,0 +1,8 @@
1
+ <% chart_id = SecureRandom.hex %>
2
+ <%= tag.div id: chart_id, style: "height: 300px;" %>
3
+ <%= javascript_tag nonce: true do %>
4
+ (function () {
5
+ <%= blazer_js_var "chart", {type: type, id: chart_id, data: data, options: options} %>
6
+ new Chartkick[chart.type](chart.id, chart.data, chart.options);
7
+ })()
8
+ <% end %>
@@ -4,29 +4,29 @@
4
4
 
5
5
  <% @variable_params = @query.persisted? ? variable_params(@query) : nested_variable_params(@query) %>
6
6
 
7
- <div id="app" v-cloak>
7
+ <div id="app" class="hide">
8
8
  <%= form_for @query, url: (@query.persisted? ? query_path(@query, params: @variable_params) : queries_path(params: @variable_params)), html: {autocomplete: "off"} do |f| %>
9
9
  <div class="row">
10
10
  <div id="statement-box" class="col-xs-8">
11
11
  <div class= "form-group">
12
12
  <%= f.hidden_field :statement %>
13
13
  <div id="editor-container">
14
- <div id="editor" :style="{ height: editorHeight }"><%= @query.statement %></div>
14
+ <div id="editor"><%= @query.statement %></div>
15
15
  </div>
16
16
  </div>
17
17
  <div class="form-group text-right" style="margin-bottom: 8px;">
18
18
  <div class="pull-left" style="margin-top: 8px;">
19
19
  <%= link_to "Back", :back %>
20
- <a :href="docsPath" target="_blank" style="margin-left: 40px;">Docs</a>
21
- <a :href="schemaPath" target="_blank" style="margin-left: 40px;">Schema</a>
20
+ <a id="docs-link" target="_blank" style="margin-left: 40px;">Docs</a>
21
+ <a id="schema-link" target="_blank" style="margin-left: 40px;">Schema</a>
22
22
  </div>
23
23
 
24
24
  <%= f.select :data_source, Blazer.data_sources.map { |_, ds| [ds.name, ds.id] }, {}, class: ("hide" if Blazer.data_sources.size <= 1), style: "width: 140px;" %>
25
25
  <div id="tables" style="display: inline-block; width: 250px; margin-right: 10px;">
26
26
  <select id="table_names" style="width: 240px;" placeholder="Preview table"></select>
27
27
  </div>
28
- <a v-on:click="run" v-if="!running" class="btn btn-info" style="vertical-align: top; width: 70px;">Run</a>
29
- <a v-on:click="cancel" v-if="running" class="btn btn-danger" style="vertical-align: top; width: 70px;">Cancel</a>
28
+ <a id="run-button" class="btn btn-info" style="vertical-align: top; width: 70px;">Run</a>
29
+ <a id="cancel-button" class="btn btn-danger hide" style="vertical-align: top; width: 70px;">Cancel</a>
30
30
  </div>
31
31
  </div>
32
32
  <div class="col-xs-4">
@@ -63,8 +63,8 @@
63
63
  <% end %>
64
64
 
65
65
  <div id="results">
66
- <p class="text-muted" v-if="running">Loading...</p>
67
- <div id="results-html" v-if="!running" :class="{ 'query-error': error }"></div>
66
+ <p id="loading" class="text-muted hide">Loading...</p>
67
+ <div id="results-html"></div>
68
68
  </div>
69
69
  </div>
70
70
 
@@ -72,184 +72,157 @@
72
72
  <%= blazer_js_var "variableParams", @variable_params %>
73
73
  <%= blazer_js_var "previewStatement", Blazer.data_sources.to_h { |k, v| [k, (v.preview_statement rescue "")] } %>
74
74
 
75
- var app = Vue.createApp({
76
- data: function() {
77
- return {
78
- running: false,
79
- results: "",
80
- error: false,
81
- dataSource: "",
82
- selectize: null,
83
- editorHeight: "180px"
84
- }
85
- },
86
- computed: {
87
- schemaPath: function() {
88
- return Routes.schema_queries_path({data_source: this.dataSource})
89
- },
90
- docsPath: function() {
91
- return Routes.docs_queries_path({data_source: this.dataSource})
92
- }
93
- },
94
- methods: {
95
- run: function(e) {
96
- this.running = true
97
- this.results = ""
98
- this.error = false
99
- cancelAllQueries()
100
-
101
- var data = {statement: this.getSQL(), data_source: $("#query_data_source").val(), variables: variableParams}
102
-
103
- var _this = this
104
-
105
- runQuery(data, function (data) {
106
- _this.running = false
107
- _this.showResults(data)
108
-
109
- errorLine = _this.getErrorLine()
110
- if (errorLine) {
111
- editor.getSession().addGutterDecoration(errorLine - 1, "error")
112
- editor.scrollToLine(errorLine, true, true, function () {})
113
- editor.gotoLine(errorLine, 0, true)
114
- editor.focus()
115
- }
116
- }, function (data) {
117
- _this.running = false
118
- _this.error = true
119
- _this.showResults(data)
120
- })
121
- },
122
- cancel: function(e) {
123
- this.running = false
124
- cancelAllQueries()
125
- },
126
- updateDataSource: function(dataSource) {
127
- this.dataSource = dataSource
128
- var selectize = this.selectize
129
- selectize.clearOptions()
75
+ const app = document.getElementById("app")
76
+ const statement = document.getElementById("query_statement")
77
+ const docsLink = document.getElementById("docs-link")
78
+ const schemaLink = document.getElementById("schema-link")
79
+ const tableSelect = document.getElementById("table_names")
80
+ const dsSelect = document.getElementById("query_data_source")
81
+ const runButton = document.getElementById("run-button")
82
+ const cancelButton = document.getElementById("cancel-button")
83
+ const results = document.getElementById("results-html")
84
+ const loading = document.getElementById("loading")
85
+ let editor
86
+ let tablesXhr
87
+
88
+ function getSQL() {
89
+ const selectedText = editor.getSelectedText()
90
+ const text = selectedText.length < 10 ? editor.getValue() : selectedText
91
+ return text.replace(/\n/g, "\r\n")
92
+ }
93
+
94
+ function startRunning() {
95
+ hide(runButton)
96
+ results.replaceChildren()
97
+ results.classList.remove("query-error")
98
+ hide(results)
99
+ show(cancelButton)
100
+ show(loading)
101
+ }
102
+
103
+ function stopRunning() {
104
+ hide(cancelButton)
105
+ hide(loading)
106
+ show(runButton)
107
+ show(results)
108
+ }
109
+
110
+ function run(e) {
111
+ startRunning()
112
+ cancelAllQueries()
113
+
114
+ const runData = {statement: getSQL(), data_source: dsSelect.value, variables: variableParams}
115
+
116
+ runQuery(runData, function (data) {
117
+ stopRunning()
118
+ renderResults(results, data)
119
+ }, function (message) {
120
+ stopRunning()
121
+ results.classList.add("query-error")
122
+ results.textContent = message
123
+ })
124
+ }
125
+
126
+ function cancel(e) {
127
+ stopRunning()
128
+ cancelAllQueries()
129
+ }
130
+
131
+ function updateDataSource() {
132
+ const dataSource = dsSelect.value
133
+
134
+ docsLink.setAttribute("href", Routes.docs_queries_path({data_source: dataSource}))
135
+ schemaLink.setAttribute("href", Routes.schema_queries_path({data_source: dataSource}))
136
+
137
+ tableSelect.tomselect.clearOptions()
138
+
139
+ if (tablesXhr) {
140
+ tablesXhr.abort()
141
+ }
130
142
 
131
- if (this.tablesXhr) {
132
- this.tablesXhr.abort()
133
- }
143
+ tablesXhr = new AbortController()
134
144
 
135
- this.tablesXhr = $.getJSON(Routes.tables_queries_path({data_source: this.dataSource}), function(data) {
136
- var newOptions = []
137
- for (var i = 0; i < data.length; i++) {
138
- var table = data[i]
139
- if (typeof table === "object") {
140
- newOptions.push({text: table.table, value: table.value})
141
- } else {
142
- newOptions.push({text: table, value: table})
143
- }
144
- }
145
- selectize.clearOptions()
146
- selectize.addOption(newOptions)
147
- selectize.refreshOptions(false)
148
- })
149
- },
150
- showEditor: function() {
151
- var _this = this
152
-
153
- editor = ace.edit("editor")
154
- editor.setTheme("ace/theme/twilight")
155
- editor.getSession().setMode("ace/mode/sql")
156
- editor.setOptions({
157
- enableBasicAutocompletion: false,
158
- enableSnippets: false,
159
- enableLiveAutocompletion: false,
160
- highlightActiveLine: false,
161
- fontSize: 12,
162
- minLines: 10
163
- })
164
- editor.renderer.setShowGutter(true)
165
- editor.renderer.setPrintMarginColumn(false)
166
- editor.renderer.setPadding(10)
167
- editor.getSession().setUseWrapMode(true)
168
- editor.commands.addCommand({
169
- name: "run",
170
- bindKey: {win: "Ctrl-Enter", mac: "Command-Enter"},
171
- exec: function(editor) {
172
- _this.run()
173
- },
174
- readOnly: false // false if this command should not apply in readOnly mode
175
- })
176
- // fix command+L
177
- editor.commands.removeCommands(["gotoline", "find"])
178
-
179
- this.editor = editor
180
-
181
- editor.getSession().on("change", function () {
182
- $("#query_statement").val(editor.getValue())
183
- _this.adjustHeight()
184
- })
185
- this.adjustHeight()
186
- editor.focus()
187
- },
188
- adjustHeight: function() {
189
- // https://stackoverflow.com/questions/11584061/
190
- var editor = this.editor
191
- var lines = editor.getSession().getScreenLength()
192
- if (lines < 9) {
193
- lines = 9
145
+ getJSON(Routes.tables_queries_path({data_source: dataSource}), function (data) {
146
+ const newOptions = []
147
+ for (const table of data) {
148
+ if (typeof table === "object") {
149
+ newOptions.push({text: table.table, value: table.value})
150
+ } else {
151
+ newOptions.push({text: table, value: table})
194
152
  }
153
+ }
154
+ tableSelect.tomselect.clearOptions()
155
+ tableSelect.tomselect.addOption(newOptions)
156
+ tableSelect.tomselect.refreshOptions(false)
157
+ }, tablesXhr)
158
+ }
159
+
160
+ function adjustHeight() {
161
+ // https://stackoverflow.com/questions/11584061/
162
+ let lines = editor.getSession().getScreenLength()
163
+ if (lines < 9) {
164
+ lines = 9
165
+ }
195
166
 
196
- this.editorHeight = ((lines + 1) * 16).toString() + "px"
197
-
198
- Vue.nextTick(function () {
199
- editor.resize()
200
- })
201
- },
202
- getSQL: function() {
203
- var selectedText = editor.getSelectedText()
204
- var text = selectedText.length < 10 ? editor.getValue() : selectedText
205
- return text.replace(/\n/g, "\r\n")
206
- },
207
- getErrorLine: function() {
208
- var editor = this.editor
209
- var errorLine = this.results.substring(0, 100).includes("alert-danger") && /LINE (\d+)/g.exec(this.results)
210
-
211
- if (errorLine) {
212
- errorLine = parseInt(errorLine[1], 10)
213
- if (editor.getSelectedText().length >= 10) {
214
- errorLine += editor.getSelectionRange().start.row
215
- }
216
- return errorLine
217
- }
167
+ editor.container.style.height = ((lines + 1) * 16).toString() + "px"
168
+ editor.resize()
169
+ }
170
+
171
+ function showEditor() {
172
+ editor = ace.edit("editor")
173
+ editor.setTheme("ace/theme/twilight")
174
+ editor.getSession().setMode("ace/mode/sql")
175
+ editor.setOptions({
176
+ enableBasicAutocompletion: false,
177
+ enableSnippets: false,
178
+ enableLiveAutocompletion: false,
179
+ highlightActiveLine: false,
180
+ fontSize: 12,
181
+ minLines: 10
182
+ })
183
+ editor.renderer.setShowGutter(true)
184
+ editor.renderer.setPrintMarginColumn(false)
185
+ editor.renderer.setPadding(10)
186
+ editor.getSession().setUseWrapMode(true)
187
+ editor.commands.addCommand({
188
+ name: "run",
189
+ bindKey: {win: "Ctrl-Enter", mac: "Command-Enter"},
190
+ exec: function (editor) {
191
+ run()
218
192
  },
219
- showResults(data) {
220
- // can't do it the Vue way due to script tags in results
221
- // this.results = data
193
+ readOnly: false // false if this command should not apply in readOnly mode
194
+ })
195
+ // fix command+L
196
+ editor.commands.removeCommands(["gotoline", "find"])
197
+
198
+ editor.getSession().on("change", function () {
199
+ statement.value = editor.getValue()
200
+ adjustHeight()
201
+ })
202
+ adjustHeight()
203
+ editor.focus()
204
+ }
205
+
206
+ runButton.addEventListener("click", run)
207
+ cancelButton.addEventListener("click", cancel)
208
+
209
+ new TomSelect(tableSelect, {
210
+ onChange: function (table) {
211
+ editor.setValue(previewStatement[dsSelect.value].replace("{table}", table), 1)
212
+ run()
213
+ this.clear(true)
214
+ this.blur()
215
+ }
216
+ })
222
217
 
223
- Vue.nextTick(function () {
224
- $("#results-html").html(data)
225
- })
226
- }
227
- },
228
- mounted: function() {
229
- var _this = this
230
-
231
- var $select = $("#table_names").selectize({})
232
- var selectize = $select[0].selectize
233
- selectize.on("change", function(val) {
234
- editor.setValue(previewStatement[_this.dataSource].replace("{table}", val), 1)
235
- _this.run()
236
- selectize.clear(true)
237
- selectize.blur()
238
- })
239
- this.selectize = selectize
240
-
241
- this.updateDataSource($("#query_data_source").val())
242
-
243
- var $dsSelect = $("#query_data_source").selectize({})
244
- var dsSelectize = $dsSelect[0].selectize
245
- dsSelectize.on("change", function(val) {
246
- _this.updateDataSource(val)
247
- dsSelectize.blur()
248
- })
249
-
250
- this.showEditor()
218
+ new TomSelect(dsSelect, {
219
+ onChange: function() {
220
+ updateDataSource()
221
+ this.blur()
251
222
  }
252
223
  })
253
- app.config.compilerOptions.whitespace = "preserve"
254
- app.mount("#app")
224
+
225
+ updateDataSource()
226
+ showEditor()
227
+ show(app)
255
228
  <% end %>
@@ -0,0 +1,8 @@
1
+ <% map_id = SecureRandom.hex %>
2
+ <%= tag.div id: map_id, style: "height: #{@only_chart ? 300 : 500}px;" %>
3
+ <%= javascript_tag nonce: true do %>
4
+ (function () {
5
+ <%= blazer_js_var "map", {type: type, id: map_id, data: data, options: options} %>
6
+ new Mapkick[map.type](map.id, map.data, map.options);
7
+ })()
8
+ <% end %>
@@ -9,7 +9,7 @@
9
9
  <% if @smart_variables.any? %>
10
10
  <p>Use these variable names to get a dropdown of values.</p>
11
11
 
12
- <table class="table" style="max-width: 500px;">
12
+ <table class="table docs-table">
13
13
  <thead>
14
14
  <tr>
15
15
  <th>Variable</th>
@@ -34,10 +34,10 @@
34
34
  <% if @linked_columns.any? %>
35
35
  <p>Use these column names to link results to other pages.</p>
36
36
 
37
- <table class="table" style="max-width: 500px;">
37
+ <table class="table docs-table">
38
38
  <thead>
39
39
  <tr>
40
- <th style="width: 20%;">Name</th>
40
+ <th class="first-header">Name</th>
41
41
  <th>URL</th>
42
42
  </tr>
43
43
  </thead>
@@ -61,7 +61,7 @@
61
61
  <% if @smart_columns.any? %>
62
62
  <p>Use these column names to show additional data.</p>
63
63
 
64
- <table class="table" style="max-width: 500px;">
64
+ <table class="table docs-table">
65
65
  <thead>
66
66
  <tr>
67
67
  <th>Name</th>
@@ -83,10 +83,10 @@
83
83
 
84
84
  <p>Use specific combinations of column types to generate charts.</p>
85
85
 
86
- <table class="table" style="max-width: 500px;">
86
+ <table class="table docs-table">
87
87
  <thead>
88
88
  <tr>
89
- <th style="width: 20%;">Chart</th>
89
+ <th class="first-header">Chart</th>
90
90
  <th>Column Types</th>
91
91
  </tr>
92
92
  </thead>
@@ -1,2 +1,3 @@
1
1
  <% blazer_title "Edit - #{@query.name}" %>
2
+
2
3
  <%= render partial: "form" %>