comenzar traducción de blazer
This commit is contained in:
parent
21193a08ac
commit
4ad1339a60
3 changed files with 458 additions and 0 deletions
250
app/views/blazer/queries/_form.html.erb
Normal file
250
app/views/blazer/queries/_form.html.erb
Normal file
|
@ -0,0 +1,250 @@
|
|||
<% if @query.errors.any? %>
|
||||
<div class="alert alert-danger"><%= @query.errors.full_messages.first %></div>
|
||||
<% end %>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<%= form_for @query, url: (@query.persisted? ? query_path(@query, params: variable_params(@query)) : queries_path(params: variable_params(@query))), html: {autocomplete: "off"} do |f| %>
|
||||
<div class="row">
|
||||
<div id="statement-box" class="col-xs-8">
|
||||
<div class= "form-group">
|
||||
<%= f.hidden_field :statement %>
|
||||
<div id="editor-container">
|
||||
<div id="editor" :style="{ height: editorHeight }"><%= @query.statement %></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group text-right" style="margin-bottom: 8px;">
|
||||
<div class="pull-left" style="margin-top: 8px;">
|
||||
<%= link_to t('.back'), :back %>
|
||||
<a :href="docsPath" target="_blank" style="margin-left: 40px;"><%= t('.docs') %></a>
|
||||
<a :href="schemaPath" target="_blank" style="margin-left: 40px;"><%= t('.schema') %></a>
|
||||
</div>
|
||||
|
||||
<%= f.select :data_source, Blazer.data_sources.values.select { |ds| q = @query.dup; q.data_source = ds.id; q.editable?(blazer_user) }.map { |ds| [ds.name, ds.id] }, {}, class: ("hide" if Blazer.data_sources.size <= 1), style: "width: 140px;" %>
|
||||
<div id="tables" style="display: inline-block; width: 250px; margin-right: 10px;">
|
||||
<select id="table_names" style="width: 240px;" placeholder="<%= t('.preview_table') %>"></select>
|
||||
</div>
|
||||
<a v-on:click="run" v-if="!running" class="btn btn-info" style="vertical-align: top;"><%= t('.run') %></a>
|
||||
<a v-on:click="cancel" v-if="running" class="btn btn-danger" style="vertical-align: top;"><%= t('.cancel') %></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-4">
|
||||
<div class="form-group">
|
||||
<%= f.label :name %>
|
||||
<%= f.text_field :name, class: "form-control" %>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<%= f.label :description %>
|
||||
<%= f.text_area :description, placeholder: t('.optional'), style: "height: 80px;", class: "form-control" %>
|
||||
</div>
|
||||
<div class="form-group text-right">
|
||||
<%= f.submit "For Enter Press", class: "hide" %>
|
||||
<% if @query.persisted? %>
|
||||
<%= link_to t('.delete'), query_path(@query), method: :delete, "data-confirm" => t('.are_you_sure'), class: "btn btn-danger" %>
|
||||
<%= f.submit t('.fork'), class: "btn btn-info" %>
|
||||
<% end %>
|
||||
<%= f.submit @query.persisted? ? t('.update') : t('.create'), class: "btn btn-success" %>
|
||||
</div>
|
||||
<% if @query.persisted? %>
|
||||
<% dashboards_count = @query.dashboards.count %>
|
||||
<% checks_count = @query.checks.count %>
|
||||
<% words = [] %>
|
||||
<% words << pluralize(dashboards_count, "dashboard") if dashboards_count > 0 %>
|
||||
<% words << pluralize(checks_count, "check") if checks_count > 0 %>
|
||||
<% if words.any? %>
|
||||
<div class="alert alert-info">
|
||||
<%= t('.part_of', words: words.to_sentence) %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div id="results">
|
||||
<p class="text-muted" v-if="running"><%= t('.loading') %></p>
|
||||
<div id="results-html" v-if="!running" :class="{ 'query-error': error }"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<%= blazer_js_var "variableParams", variable_params(@query) %>
|
||||
<%= blazer_js_var "previewStatement", Hash[Blazer.data_sources.map { |k, v| [k, (v.preview_statement rescue "")] }] %>
|
||||
|
||||
var app = new Vue({
|
||||
el: "#app",
|
||||
data: {
|
||||
running: false,
|
||||
results: "",
|
||||
error: false,
|
||||
dataSource: "",
|
||||
selectize: null,
|
||||
editorHeight: "180px"
|
||||
},
|
||||
computed: {
|
||||
schemaPath: function() {
|
||||
return Routes.schema_queries_path({data_source: this.dataSource})
|
||||
},
|
||||
docsPath: function() {
|
||||
return Routes.docs_queries_path({data_source: this.dataSource})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
run: function(e) {
|
||||
this.running = true
|
||||
this.results = ""
|
||||
this.error = false
|
||||
cancelAllQueries()
|
||||
|
||||
var data = {statement: this.getSQL(), data_source: $("#query_data_source").val(), variables: variableParams}
|
||||
|
||||
var _this = this
|
||||
|
||||
runQuery(data, function (data) {
|
||||
_this.running = false
|
||||
_this.showResults(data)
|
||||
|
||||
errorLine = _this.getErrorLine()
|
||||
if (errorLine) {
|
||||
editor.getSession().addGutterDecoration(errorLine - 1, "error")
|
||||
editor.scrollToLine(errorLine, true, true, function () {})
|
||||
editor.gotoLine(errorLine, 0, true)
|
||||
editor.focus()
|
||||
}
|
||||
}, function (data) {
|
||||
_this.running = false
|
||||
_this.error = true
|
||||
_this.showResults(data)
|
||||
})
|
||||
},
|
||||
cancel: function(e) {
|
||||
this.running = false
|
||||
cancelAllQueries()
|
||||
},
|
||||
updateDataSource: function(dataSource) {
|
||||
this.dataSource = dataSource
|
||||
var selectize = this.selectize
|
||||
selectize.clearOptions()
|
||||
|
||||
if (this.tablesXhr) {
|
||||
this.tablesXhr.abort()
|
||||
}
|
||||
|
||||
this.tablesXhr = $.getJSON(Routes.tables_queries_path({data_source: this.dataSource}), function(data) {
|
||||
var newOptions = []
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var table = data[i]
|
||||
if (typeof table === "object") {
|
||||
newOptions.push({text: table.table, value: table.value})
|
||||
} else {
|
||||
newOptions.push({text: table, value: table})
|
||||
}
|
||||
}
|
||||
selectize.clearOptions()
|
||||
selectize.addOption(newOptions)
|
||||
selectize.refreshOptions(false)
|
||||
})
|
||||
},
|
||||
showEditor: function() {
|
||||
var _this = this
|
||||
|
||||
editor = ace.edit("editor")
|
||||
editor.setTheme("ace/theme/twilight")
|
||||
editor.getSession().setMode("ace/mode/sql")
|
||||
editor.setOptions({
|
||||
enableBasicAutocompletion: false,
|
||||
enableSnippets: false,
|
||||
enableLiveAutocompletion: false,
|
||||
highlightActiveLine: false,
|
||||
fontSize: 12,
|
||||
minLines: 10
|
||||
})
|
||||
editor.renderer.setShowGutter(true)
|
||||
editor.renderer.setPrintMarginColumn(false)
|
||||
editor.renderer.setPadding(10)
|
||||
editor.getSession().setUseWrapMode(true)
|
||||
editor.commands.addCommand({
|
||||
name: "run",
|
||||
bindKey: {win: "Ctrl-Enter", mac: "Command-Enter"},
|
||||
exec: function(editor) {
|
||||
_this.run()
|
||||
},
|
||||
readOnly: false // false if this command should not apply in readOnly mode
|
||||
})
|
||||
// fix command+L
|
||||
editor.commands.removeCommands(["gotoline", "find"])
|
||||
|
||||
this.editor = editor
|
||||
|
||||
editor.getSession().on("change", function () {
|
||||
$("#query_statement").val(editor.getValue())
|
||||
_this.adjustHeight()
|
||||
})
|
||||
this.adjustHeight()
|
||||
editor.focus()
|
||||
},
|
||||
adjustHeight: function() {
|
||||
// https://stackoverflow.com/questions/11584061/
|
||||
var editor = this.editor
|
||||
var lines = editor.getSession().getScreenLength()
|
||||
if (lines < 9) {
|
||||
lines = 9
|
||||
}
|
||||
|
||||
this.editorHeight = ((lines + 1) * 16).toString() + "px"
|
||||
|
||||
Vue.nextTick(function () {
|
||||
editor.resize()
|
||||
})
|
||||
},
|
||||
getSQL: function() {
|
||||
var selectedText = editor.getSelectedText()
|
||||
var text = selectedText.length < 10 ? editor.getValue() : selectedText
|
||||
return text.replace(/\n/g, "\r\n")
|
||||
},
|
||||
getErrorLine: function() {
|
||||
var editor = this.editor
|
||||
var errorLine = this.results.substring(0, 100).includes("alert-danger") && /LINE (\d+)/g.exec(this.results)
|
||||
|
||||
if (errorLine) {
|
||||
errorLine = parseInt(errorLine[1], 10)
|
||||
if (editor.getSelectedText().length >= 10) {
|
||||
errorLine += editor.getSelectionRange().start.row
|
||||
}
|
||||
return errorLine
|
||||
}
|
||||
},
|
||||
showResults(data) {
|
||||
// can't do it the Vue way due to script tags in results
|
||||
// this.results = data
|
||||
|
||||
Vue.nextTick(function () {
|
||||
$("#results-html").html(data)
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
var _this = this
|
||||
|
||||
var $select = $("#table_names").selectize({})
|
||||
var selectize = $select[0].selectize
|
||||
selectize.on("change", function(val) {
|
||||
editor.setValue(previewStatement[_this.dataSource].replace("{table}", val), 1)
|
||||
_this.run()
|
||||
selectize.clear(true)
|
||||
selectize.blur()
|
||||
})
|
||||
this.selectize = selectize
|
||||
|
||||
this.updateDataSource($("#query_data_source").val())
|
||||
|
||||
var $dsSelect = $("#query_data_source").selectize({})
|
||||
var dsSelectize = $dsSelect[0].selectize
|
||||
dsSelectize.on("change", function(val) {
|
||||
_this.updateDataSource(val)
|
||||
dsSelectize.blur()
|
||||
})
|
||||
|
||||
this.showEditor()
|
||||
}
|
||||
})
|
||||
</script>
|
166
app/views/blazer/queries/home.html.erb
Normal file
166
app/views/blazer/queries/home.html.erb
Normal file
|
@ -0,0 +1,166 @@
|
|||
<div id="queries">
|
||||
<div id="header">
|
||||
<div class="pull-right" style="line-height: 34px;">
|
||||
<% if blazer_user %>
|
||||
<%= link_to t('.all'), root_path, class: !params[:filter] ? "active" : nil, style: "margin-right: 40px;" %>
|
||||
|
||||
<% if Blazer.audit %>
|
||||
<%= link_to t('.viewed'), root_path(filter: "viewed"), class: params[:filter] == "viewed" ? "active" : nil, style: "margin-right: 40px;" %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to t('.mine'), root_path(filter: "mine"), class: params[:filter] == "mine" ? "active" : nil, style: "margin-right: 40px;" %>
|
||||
<% end %>
|
||||
|
||||
<div class="btn-group">
|
||||
<%= link_to t('.new_query'), new_query_path, class: "btn btn-info" %>
|
||||
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only"><%= t('.toggle_dropdown') %></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li><%= link_to t('.checks'), checks_path %></li>
|
||||
<% if Blazer.uploads? %>
|
||||
<li><%= link_to t('.uploads'), uploads_path %></li>
|
||||
<% end %>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li><%= link_to t('.new_dashboard'), new_dashboard_path %></li>
|
||||
<li><%= link_to t('.new_check'), new_check_path %></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<input type="text" v-model="searchTerm" placeholder="<%= t('.placeholder') %>" style="width: 300px; display: inline-block;" v-focus class="search form-control" />
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><%= t('.name') %></th>
|
||||
<% if Blazer.user_class %>
|
||||
<th style="width: 20%; text-align: right;"><%= t('.mastermind') %></th>
|
||||
<% end%>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list" v-cloak>
|
||||
<tr v-for="query in visibleItems">
|
||||
<td>
|
||||
<a :href="itemPath(query)" :class="{ dashboard: query.dashboard }">{{ query.name }}</a>
|
||||
<span class="vars">{{ query.vars }}</span>
|
||||
</td>
|
||||
<% if Blazer.user_class %>
|
||||
<td class="creator">{{ query.creator }}</td>
|
||||
<% end %>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<p v-if="more" class="text-muted"><%= t('.loading') %></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
<%= blazer_js_var "dashboards", @dashboards %>
|
||||
<%= blazer_js_var "queries", @queries %>
|
||||
<%= blazer_js_var "more", @more %>
|
||||
|
||||
var prepareSearch = function (list) {
|
||||
var i, q, searchStr
|
||||
for (i = 0; i < list.length; i++) {
|
||||
q = list[i]
|
||||
searchStr = q.name + q.creator
|
||||
if (q.creator === "You") {
|
||||
searchStr += "mine me"
|
||||
}
|
||||
q.searchStr = prepareQuery(searchStr)
|
||||
}
|
||||
}
|
||||
|
||||
var prepareQuery = function (str) {
|
||||
return str.toLowerCase()
|
||||
}
|
||||
|
||||
var app = new Vue({
|
||||
el: "#queries",
|
||||
data: {
|
||||
searchTerm: "",
|
||||
more: more,
|
||||
updateCounter: 0
|
||||
},
|
||||
created: function() {
|
||||
this.listItems = dashboards.concat(queries)
|
||||
|
||||
prepareSearch(this.listItems)
|
||||
|
||||
this.queryIds = {}
|
||||
for (i = 0; i < queries.length; i++) {
|
||||
this.queryIds[queries[i].id] = true
|
||||
}
|
||||
|
||||
if (this.more) {
|
||||
var _this = this
|
||||
|
||||
$.getJSON(Routes.queries_path(), function (data) {
|
||||
var i, j, newValues, val, size = 500;
|
||||
|
||||
var newValues = []
|
||||
for (j = 0; j < data.length; j++) {
|
||||
val = data[j]
|
||||
if (val && !_this.queryIds[val.id]) {
|
||||
newValues.push(val)
|
||||
}
|
||||
}
|
||||
|
||||
prepareSearch(newValues)
|
||||
|
||||
_this.listItems = _this.listItems.concat(newValues)
|
||||
_this.more = false
|
||||
// hack to get to update
|
||||
_this.updateCounter++
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
visibleItems: function () {
|
||||
// hack to get to update
|
||||
this.updateCounter
|
||||
|
||||
var pageSize = 200
|
||||
var q, i
|
||||
|
||||
if (this.searchTerm.length > 0) {
|
||||
var term = prepareQuery(this.searchTerm)
|
||||
var items = []
|
||||
var fuzzyItems = []
|
||||
for (i = 0; i < this.listItems.length; i++) {
|
||||
q = this.listItems[i]
|
||||
if (q.searchStr.indexOf(term) !== -1) {
|
||||
items.push(q)
|
||||
if (items.length == pageSize) {
|
||||
break
|
||||
}
|
||||
} else if (fuzzysearch(term, q.searchStr)) {
|
||||
fuzzyItems.push(q)
|
||||
}
|
||||
}
|
||||
return items.concat(fuzzyItems).slice(0, pageSize)
|
||||
} else {
|
||||
return this.listItems.slice(0, pageSize)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
itemPath: function (item) {
|
||||
if (item.dashboard) {
|
||||
return Routes.dashboard_path(item.to_param)
|
||||
} else {
|
||||
return Routes.query_path(item.to_param)
|
||||
}
|
||||
}
|
||||
},
|
||||
directives: {
|
||||
focus: {
|
||||
inserted: function (el) {
|
||||
el.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
42
config/locales/es.yml
Normal file
42
config/locales/es.yml
Normal file
|
@ -0,0 +1,42 @@
|
|||
es:
|
||||
activerecord:
|
||||
models:
|
||||
query: Consulta
|
||||
attributes:
|
||||
query:
|
||||
name: Nombre
|
||||
description: Descripción
|
||||
blazer:
|
||||
queries:
|
||||
home:
|
||||
all: Todas
|
||||
viewed: Vistas
|
||||
mine: Mías
|
||||
new_query: Crear consulta
|
||||
toggle_dropdown: Desplegar opciones
|
||||
checks: Alertas
|
||||
uploads: Subida de archivos
|
||||
new_dashboard: Crear resumen
|
||||
new_check: Crear alerta
|
||||
placeholder: Empieza a escribir para encontrar
|
||||
name: Nombre
|
||||
mastermind: Editorx
|
||||
loading: Cargando...
|
||||
new:
|
||||
new_query: Crear consulta
|
||||
form:
|
||||
back: Volver
|
||||
docs: Documentación
|
||||
schema: Esquema
|
||||
preview_table: Previsualizar la tabla
|
||||
run: Resultados
|
||||
cancel: Cancelar
|
||||
optional: Opcional
|
||||
delete: Eliminar
|
||||
are_you_sure: ¿Confirmas que quieres eliminar esta consulta?
|
||||
fork: Clonar
|
||||
update: Guardar cambios
|
||||
create: Guardar
|
||||
part_of: Es parte de %{words}. Cualquier cambio puede afectarlas.
|
||||
loading: Cargando...
|
||||
|
Loading…
Reference in a new issue