_form.html.erb 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <% if @query.errors.any? %>
  2. <div class="alert alert-danger"><%= @query.errors.full_messages.first %></div>
  3. <% end %>
  4. <%= form_for @query, url: (@query.persisted? ? query_path(@query, variable_params) : queries_path(variable_params)), html: {class: "the_form", autocomplete: "off"} do |f| %>
  5. <div class="row">
  6. <div id="statement-box" class="col-xs-8">
  7. <div class= "form-group">
  8. <%= f.hidden_field :statement %>
  9. <div id="editor-container">
  10. <div id="editor"><%= @query.statement %></div>
  11. </div>
  12. </div>
  13. <div class="form-group text-right">
  14. <div class="pull-left" style="margin-top: 6px;">
  15. <%= link_to "Back", :back %>
  16. </div>
  17. <%= f.select :data_source, Blazer.data_sources.values.map { |ds| [ds.name, ds.id] }, {}, class: ("hide" if Blazer.data_sources.size == 1), style: "width: 140px;" %>
  18. <div id="tables" style="display: inline-block; width: 250px; margin-right: 10px;" class="hide">
  19. <%= render partial: "tables" %>
  20. </div>
  21. <script>
  22. function updatePreviewSelect() {
  23. $("#tables").load("<%= tables_queries_path %>?" + $.param({data_source: $("#query_data_source").val()}));
  24. }
  25. updatePreviewSelect();
  26. $("#query_data_source").selectize().change(updatePreviewSelect);
  27. </script>
  28. <%= link_to "Run", "#", class: "btn btn-info", id: "run", style: "vertical-align: top; width: 70px;" %>
  29. <%= link_to "Cancel", "#", class: "btn btn-danger hide", id: "cancel", style: "vertical-align: top; width: 70px;" %>
  30. </div>
  31. </div>
  32. <div class="col-xs-4">
  33. <div class="form-group">
  34. <%= f.label :name %>
  35. <%= f.text_field :name, class: "form-control" %>
  36. </div>
  37. <div class="form-group">
  38. <%= f.label :description %>
  39. <%= f.text_area :description, placeholder: "Optional", style: "height: 80px;", class: "form-control" %>
  40. </div>
  41. <div class="text-right">
  42. <% if @query.persisted? %>
  43. <%= link_to "Delete", query_path(@query), method: :delete, "data-confirm" => "Are you sure?", class: "btn btn-danger" %>
  44. <%= f.submit "Fork", class: "btn btn-info" %>
  45. <% end %>
  46. <%= f.submit @query.persisted? ? "Update" : "Create", class: "btn btn-success" %>
  47. </div>
  48. <% if @query.persisted? %>
  49. <% dashboards_count = @query.dashboards.count %>
  50. <% checks_count = @query.checks.count %>
  51. <% words = [] %>
  52. <% words << pluralize(dashboards_count, "dashboard") if dashboards_count > 0 %>
  53. <% words << pluralize(checks_count, "check") if checks_count > 0 %>
  54. <% if words.any? %>
  55. <div class="alert alert-info" style="margin-top: 10px; padding: 8px 12px;">
  56. Part of <%= words.to_sentence %>. Be careful when editing.
  57. </div>
  58. <% end %>
  59. <% end %>
  60. </div>
  61. </div>
  62. <% end %>
  63. <div id="results"></div>
  64. <script>
  65. var editor = ace.edit("editor");
  66. editor.setTheme("ace/theme/twilight");
  67. editor.getSession().setMode("ace/mode/sql");
  68. editor.setOptions({
  69. enableBasicAutocompletion: false,
  70. enableSnippets: false,
  71. enableLiveAutocompletion: false,
  72. highlightActiveLine: false,
  73. fontSize: 12,
  74. minLines: 10
  75. });
  76. editor.renderer.setShowGutter(true);
  77. editor.renderer.setPrintMarginColumn(false);
  78. editor.renderer.setPadding(10);
  79. editor.getSession().setUseWrapMode(true);
  80. editor.commands.addCommand({
  81. name: 'run',
  82. bindKey: {win: 'Ctrl-Enter', mac: 'Command-Enter'},
  83. exec: function(editor) {
  84. $("#run").click();
  85. },
  86. readOnly: false // false if this command should not apply in readOnly mode
  87. });
  88. // http://stackoverflow.com/questions/11584061/
  89. function adjustHeight() {
  90. var lines = editor.getSession().getScreenLength();
  91. if (lines < 9) {
  92. lines = 9;
  93. }
  94. var newHeight = (lines + 1) * 16;
  95. $("#editor").height(newHeight.toString() + "px");
  96. editor.resize();
  97. };
  98. function getSQL() {
  99. var selectedText = editor.getSelectedText();
  100. var text = selectedText.length < 10 ? editor.getValue() : selectedText;
  101. return text.replace(/\n/g, "\r\n");
  102. }
  103. function getErrorLine() {
  104. var error_line = /LINE (\d+)/g.exec($("#results").find('.alert-danger').text());
  105. if (error_line) {
  106. error_line = parseInt(error_line[1], 10);
  107. if (editor.getSelectedText().length >= 10) {
  108. error_line += editor.getSelectionRange().start.row;
  109. }
  110. return error_line;
  111. }
  112. }
  113. editor.getSession().on("change", adjustHeight);
  114. adjustHeight();
  115. $("#editor").show();
  116. editor.focus();
  117. var error_line = null;
  118. var runningQuery;
  119. var params = <%= raw blazer_json_escape(variable_params.to_json) %>;
  120. var previewStatement = <%= raw blazer_json_escape(Hash[Blazer.data_sources.map { |k, v| [k, v.preview_statement] }].to_json) %>;
  121. function queryDone() {
  122. runningQuery = null
  123. $("#run").removeClass("hide")
  124. $("#cancel").addClass("hide")
  125. }
  126. $("#cancel").click( function (e) {
  127. e.preventDefault()
  128. cancelQuery(runningQuery)
  129. queryDone()
  130. $("#results").html("")
  131. })
  132. $(window).unload(function() {
  133. if (runningQuery) {
  134. remoteCancelQuery(runningQuery)
  135. }
  136. })
  137. $("#run").click( function (e) {
  138. e.preventDefault();
  139. $(this).addClass("hide")
  140. $("#cancel").removeClass("hide")
  141. if (error_line) {
  142. editor.getSession().removeGutterDecoration(error_line - 1, "error");
  143. error_line = null;
  144. }
  145. $("#results").html('<p class="text-muted">Loading...</p>');
  146. var data = $.extend({}, params, {statement: getSQL(), data_source: $("#query_data_source").val()});
  147. runningQuery = {};
  148. runQuery(data, function (data) {
  149. queryDone()
  150. $("#results").html(data);
  151. error_line = getErrorLine();
  152. if (error_line) {
  153. editor.getSession().addGutterDecoration(error_line - 1, "error");
  154. editor.scrollToLine(error_line, true, true, function () {});
  155. editor.gotoLine(error_line, 0, true);
  156. editor.focus();
  157. }
  158. }, function (data) {
  159. // TODO show error
  160. queryDone()
  161. }, runningQuery);
  162. });
  163. $(document).on("change", "#table_names", function () {
  164. var val = $(this).val();
  165. if (val.length > 0) {
  166. var dataSource = $("#query_data_source").val();
  167. editor.setValue(previewStatement[dataSource].replace("{table}", val), 1);
  168. $("#run").click();
  169. }
  170. });
  171. $("form.the_form").on("submit", function() {
  172. $("#query_statement").val(editor.getValue());
  173. return true;
  174. });
  175. preventBackspaceNav();
  176. </script>