motor-admin 0.1.102 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/concerns/motor/load_and_authorize_dynamic_resource.rb +3 -2
- data/app/controllers/motor/data_controller.rb +14 -2
- data/app/controllers/motor/ui_controller.rb +13 -1
- data/app/mailers/motor/alerts_mailer.rb +1 -1
- data/app/views/motor/ui/show.html.erb +1 -1
- data/config/locales/en.yml +7 -1
- data/config/locales/es.yml +7 -1
- data/config/locales/pt.yml +345 -0
- data/config/routes.rb +2 -1
- data/lib/generators/motor/templates/install.rb +9 -0
- data/lib/motor/active_record_utils/active_record_filter_patch.rb +12 -0
- data/lib/motor/api_query/build_json.rb +1 -1
- data/lib/motor/build_schema/load_from_rails.rb +16 -8
- data/lib/motor/build_schema/utils.rb +1 -0
- data/lib/motor/build_schema.rb +4 -0
- data/lib/motor/configs/build_ui_app_tag.rb +5 -3
- data/lib/motor/configs/sync_from_file.rb +1 -2
- data/lib/motor/configs/write_to_file.rb +1 -2
- data/lib/motor/configs.rb +13 -0
- data/lib/motor/queries/run_query.rb +8 -4
- data/lib/motor/resources/fetch_configured_model.rb +18 -9
- data/lib/motor/version.rb +1 -1
- data/ui/dist/main-5c903ff9235e54ac03f1.css.gz +0 -0
- data/ui/dist/main-5c903ff9235e54ac03f1.js.gz +0 -0
- data/ui/dist/manifest.json +5 -5
- metadata +5 -4
- data/ui/dist/main-61a9a4d250915792b3e7.css.gz +0 -0
- data/ui/dist/main-61a9a4d250915792b3e7.js.gz +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7adc26b7ddd5925fb9fd1aebcb3c39fd34e82641f53f379e524df989630533de
|
4
|
+
data.tar.gz: 59de79e99d4694a42cfebdf58bdad6cb1e486bdb12a13d726829949e7e7791ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da9f42328e648604b9adf63124e5c1d6d5f0a3e78d554bede10acdafef4bd14711b2404e5cc89b9d10db093637a9c9cb83af829f33d69dea94550016f80426ea
|
7
|
+
data.tar.gz: 3f3cb870e4126692d2e9e1edc584dc01c458b57bb9d805e90ddb9446d02b2b594368607d07d1f1b529ad66490749e7eb7a2bab78495f681d5889a045fc9c99f5
|
@@ -5,6 +5,7 @@ module Motor
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
INSTANCE_VARIABLE_NAME = 'resource'
|
8
|
+
ASSOCIATION_INSTANCE_VARIABLE_NAME = 'associated_resource'
|
8
9
|
|
9
10
|
included do
|
10
11
|
before_action :load_and_authorize_resource
|
@@ -26,7 +27,7 @@ module Motor
|
|
26
27
|
def load_and_authorize_resource
|
27
28
|
options = {
|
28
29
|
class: resource_class,
|
29
|
-
parent:
|
30
|
+
parent: params[:association].present?,
|
30
31
|
instance_name: INSTANCE_VARIABLE_NAME
|
31
32
|
}
|
32
33
|
|
@@ -57,7 +58,7 @@ module Motor
|
|
57
58
|
parent: false,
|
58
59
|
through: :resource,
|
59
60
|
through_association: params[:association].to_sym,
|
60
|
-
instance_name: INSTANCE_VARIABLE_NAME
|
61
|
+
instance_name: params[:action] == 'create' ? ASSOCIATION_INSTANCE_VARIABLE_NAME : INSTANCE_VARIABLE_NAME
|
61
62
|
).load_and_authorize_resource
|
62
63
|
else
|
63
64
|
render json: { message: 'Unknown association' }, status: :not_found
|
@@ -21,7 +21,19 @@ module Motor
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def create
|
24
|
-
@
|
24
|
+
if @associated_resource
|
25
|
+
if @resource_class.reflections[params[:association]].through_reflection?
|
26
|
+
@associated_resource.save!
|
27
|
+
|
28
|
+
@resource = @associated_resource
|
29
|
+
else
|
30
|
+
@resource.public_send(params[:association].to_sym).create!(@associated_resource.attributes) do |resource|
|
31
|
+
@resource = resource
|
32
|
+
end
|
33
|
+
end
|
34
|
+
else
|
35
|
+
@resource.save!
|
36
|
+
end
|
25
37
|
|
26
38
|
render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
|
27
39
|
rescue ActiveRecord::RecordInvalid
|
@@ -61,7 +73,7 @@ module Motor
|
|
61
73
|
|
62
74
|
def resource_params
|
63
75
|
if params[:data].present?
|
64
|
-
params.require(:data).
|
76
|
+
params.require(:data).permit!
|
65
77
|
else
|
66
78
|
{}
|
67
79
|
end
|
@@ -4,7 +4,9 @@ module Motor
|
|
4
4
|
class UiController < ApplicationController
|
5
5
|
layout 'motor/application'
|
6
6
|
|
7
|
-
helper_method :current_user, :current_ability
|
7
|
+
helper_method :current_user, :current_ability, :cache_keys
|
8
|
+
|
9
|
+
before_action :set_i18n_locale
|
8
10
|
|
9
11
|
def index
|
10
12
|
render_ui
|
@@ -27,5 +29,15 @@ module Motor
|
|
27
29
|
|
28
30
|
render :show
|
29
31
|
end
|
32
|
+
|
33
|
+
def set_i18n_locale
|
34
|
+
configs = Motor::Configs::LoadFromCache.load_configs(cache_key: cache_keys[:configs])
|
35
|
+
|
36
|
+
I18n.locale = configs.find { |c| c.key == 'language' }&.value || I18n.locale
|
37
|
+
end
|
38
|
+
|
39
|
+
def cache_keys
|
40
|
+
@cache_keys ||= Configs::LoadFromCache.load_cache_keys
|
41
|
+
end
|
30
42
|
end
|
31
43
|
end
|
@@ -26,7 +26,7 @@ module Motor
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def from_address
|
29
|
-
from = ENV['MOTOR_ALERTS_FROM_ADDRESS'].presence
|
29
|
+
from = ENV['MOTOR_ALERTS_FROM_ADDRESS'].presence || ENV['MOTOR_EMAIL_ADDRESS'].presence
|
30
30
|
|
31
31
|
from ||= application_mailer_default_from
|
32
32
|
from ||= mailer_config_from_address
|
@@ -1 +1 @@
|
|
1
|
-
<%= raw(Motor::Configs::BuildUiAppTag.call(current_user, current_ability)) %>
|
1
|
+
<%= raw(Motor::Configs::BuildUiAppTag.call(current_user, current_ability, cache_keys: cache_keys)) %>
|
data/config/locales/en.yml
CHANGED
@@ -82,9 +82,12 @@ en:
|
|
82
82
|
field_is_required: '%{field} is required'
|
83
83
|
field_list_cant_be_empty: "%{field} list can't be empty"
|
84
84
|
field_must_be_exactly_in_length: '%{field} must be exactly %{length} in length'
|
85
|
+
field_must_be_less_in_length: '%{field} must be less than %{length} in length'
|
86
|
+
field_must_be_more_in_length: '%{field} must be more than %{length} in length'
|
85
87
|
field_name: Field name
|
86
88
|
field_value_does_not_match_pattern: '%{field} value does not match %{pattern}'
|
87
89
|
file: File
|
90
|
+
filter: Filter
|
88
91
|
filters: Filters
|
89
92
|
form: Form
|
90
93
|
form_has_been_saved: Form has been saved!
|
@@ -108,7 +111,7 @@ en:
|
|
108
111
|
interval: Interval
|
109
112
|
is: Is
|
110
113
|
is_not: Is not
|
111
|
-
|
114
|
+
items_have_been_removed: items have been removed
|
112
115
|
items_will_be_removed: '%{count} items will be removed'
|
113
116
|
json: JSON
|
114
117
|
label: Currency
|
@@ -256,3 +259,6 @@ en:
|
|
256
259
|
add_text: Add Text
|
257
260
|
edit_text: Edit Text
|
258
261
|
open_in_markdown_editor: Open in markdown editor
|
262
|
+
activate: Activate
|
263
|
+
load_existing_options_from_database: Load existing options from the database
|
264
|
+
add_database: Add Database
|
data/config/locales/es.yml
CHANGED
@@ -59,6 +59,7 @@ es:
|
|
59
59
|
emails: Emails
|
60
60
|
every_day_at_hh_mm: todos los días a las HH:mm PM...
|
61
61
|
field_name: Nombre del campo
|
62
|
+
filter: Filtrar
|
62
63
|
filters: Filtros
|
63
64
|
form: Formulario
|
64
65
|
form_has_been_saved: ¡Formulario guardado!
|
@@ -73,7 +74,7 @@ es:
|
|
73
74
|
hello_admin: Hola Admin 👋
|
74
75
|
input_type: Tipo de campo
|
75
76
|
interval: Intervalo
|
76
|
-
|
77
|
+
items_have_been_removed: elementos fueron eliminados
|
77
78
|
label: Moneda
|
78
79
|
line_chart: Gráfico de líneas
|
79
80
|
link_name: Nombre del link
|
@@ -179,6 +180,8 @@ es:
|
|
179
180
|
field_is_required: '%{field} es obligatorio'
|
180
181
|
field_list_cant_be_empty: "La lista %{field} no puede estar vacía"
|
181
182
|
field_must_be_exactly_in_length: '%{field} debe tener un largo de %{length} caracteres'
|
183
|
+
field_must_be_less_in_length: '%{field} debe tener una longitud inferior a %{length}'
|
184
|
+
field_must_be_more_in_length: '%{field} debe tener una longitud superior a %{length}'
|
182
185
|
field_value_does_not_match_pattern: 'El valor de %{field} no coincide con %{pattern}'
|
183
186
|
file: Archivo
|
184
187
|
greater_or_equal: Mayor o igual
|
@@ -256,6 +259,9 @@ es:
|
|
256
259
|
add_text: Añadir Texto
|
257
260
|
edit_text: Editar Texto
|
258
261
|
open_in_markdown_editor: Abrir en el editor de markdown
|
262
|
+
activate: Activar
|
263
|
+
load_existing_options_from_database: Cargar las opciones existentes de la base de datos
|
264
|
+
add_database: Añadir base de datos
|
259
265
|
i:
|
260
266
|
locale: es
|
261
267
|
select:
|
@@ -0,0 +1,345 @@
|
|
1
|
+
pt:
|
2
|
+
motor:
|
3
|
+
action_has_been_applied: "A ação foi aplicada!"
|
4
|
+
action_has_failed_with_code: "A ação falhou com o código"
|
5
|
+
action_type: "Tipo de ação"
|
6
|
+
actions: "Ações"
|
7
|
+
add: "Adicionar"
|
8
|
+
add_action: "Adicionar ação"
|
9
|
+
add_alert: "Adicionar alerta"
|
10
|
+
add_association: "Adicionar associação"
|
11
|
+
add_column: "Adicionar Coluna"
|
12
|
+
add_dashboard: "Adicionar painel"
|
13
|
+
add_field: "Adicionar campo"
|
14
|
+
add_filter: "Adicionar filtro"
|
15
|
+
add_form: "Adicionar formulário"
|
16
|
+
add_group: "Adicionar grupo"
|
17
|
+
add_item: "Adicionar item"
|
18
|
+
add_link: "Adicionar link"
|
19
|
+
add_query: "Adicionar consulta"
|
20
|
+
add_scope: "Adicionar escopo"
|
21
|
+
add_tab: "Adicionar guia"
|
22
|
+
add_text: "Adicionar texto"
|
23
|
+
adjust_fields: "Ajustar campos"
|
24
|
+
adjust_form: "Ajustar formulário"
|
25
|
+
alert_email_has_been_sent: "E-mail de alerta foi enviado!"
|
26
|
+
alert_has_been_activated: "O alerta foi ativado"
|
27
|
+
alert_has_been_disabled: "O alerta foi desativado"
|
28
|
+
alert_has_been_saved: "O alerta foi salvo!"
|
29
|
+
alert_name: "Nome do alerta"
|
30
|
+
alerts: "Alertas"
|
31
|
+
all: "Todos"
|
32
|
+
all_resources: "Todos os recursos"
|
33
|
+
and: "e"
|
34
|
+
api: "API"
|
35
|
+
api_path: "Caminho da API"
|
36
|
+
api_request: "Solicitação de API"
|
37
|
+
apply: "Aplicar"
|
38
|
+
are_you_sure: "Tem certeza?"
|
39
|
+
associations: "Associações"
|
40
|
+
bar_chart: "Gráfico de barras"
|
41
|
+
base: "Base"
|
42
|
+
boolean: "Booleano"
|
43
|
+
build_custom_form: "Crie um formulário personalizado"
|
44
|
+
cancel: "Cancelar"
|
45
|
+
cents: "Centavos"
|
46
|
+
change: "Alterar"
|
47
|
+
chart: "Gráfico"
|
48
|
+
checkbox: "Caixa de seleção"
|
49
|
+
clear: "Limpar"
|
50
|
+
clear_all: "Limpar tudo"
|
51
|
+
clear_selection: "Limpar seleção"
|
52
|
+
close: "Fechar"
|
53
|
+
close_editor: "Fechar editor"
|
54
|
+
close_settings: "Fechar ajustes"
|
55
|
+
close_variables: "Fechar variáveis"
|
56
|
+
color: "Cor"
|
57
|
+
columns: "Colunas"
|
58
|
+
condition: "Condição"
|
59
|
+
conditional: "Condicional"
|
60
|
+
contains: "Contém"
|
61
|
+
copied_to_the_clipboard: "Copiado para a área de transferência"
|
62
|
+
create: "Criar"
|
63
|
+
create_new: "Criar novo"
|
64
|
+
currency: "Moeda"
|
65
|
+
current_user_variables_are_always_passed_explicitly_and_can_he_used_html: "<code>{{current_user_id}}</code> e <code>{{current_user_email}}</code> variáveis são sempre passadas explicitamente e podem ser usadas para decidir quais dados devem ser exibidos para determinado usuário:"
|
66
|
+
dashboard: "Painel"
|
67
|
+
dashboard_has_been_saved: "O painel foi salvo!"
|
68
|
+
dashboard_title: "Título do painel"
|
69
|
+
dashboards: "Painéis"
|
70
|
+
date: "Data"
|
71
|
+
date_and_time: "Data e hora"
|
72
|
+
decimal: "Decimal"
|
73
|
+
default: "Padrão"
|
74
|
+
default_value: "Valor predefinido"
|
75
|
+
describe_this_alert_optional: "Descreva este alerta (opcional)"
|
76
|
+
describe_your_dashboard_optional: "Descreva seu painel (opcional)"
|
77
|
+
describe_your_form_optional: "Descreva seu formulário (opcional)"
|
78
|
+
describe_your_query_optional: "Descreva sua consulta (opcional)"
|
79
|
+
description: "Descrição"
|
80
|
+
deselect_all: "Desmarcar tudo"
|
81
|
+
details: "Detalhes"
|
82
|
+
disable: "Desabilitar"
|
83
|
+
display_as: "Exibir como"
|
84
|
+
edit: "Editar"
|
85
|
+
edit_markdown: "Editar markdown"
|
86
|
+
edit_sql: "Editar SQL"
|
87
|
+
edit_text: "Editar texto"
|
88
|
+
emails: "E-mails"
|
89
|
+
empty: "Vazio"
|
90
|
+
ends_with: "Termina com"
|
91
|
+
equal_to: "É igual a"
|
92
|
+
every_day_at_hh_mm: "todos os dias em HH:mm PM..."
|
93
|
+
excludes: "exclui"
|
94
|
+
field: "Campo"
|
95
|
+
field_is_not_a_number: "% {field} não é um número"
|
96
|
+
field_is_required: "%{field} é obrigatório"
|
97
|
+
field_list_cant_be_empty: "A lista %{field} não pode estar vazia"
|
98
|
+
field_must_be_exactly_in_length: "%{field} deve ter exatamente %{length} de comprimento"
|
99
|
+
field_must_be_less_in_length: '%{field} deve ser menor que %{length} em comprimento'
|
100
|
+
field_must_be_more_in_length: '%{field} deve ter mais do que %{length} de comprimento'
|
101
|
+
field_name: "Nome do campo"
|
102
|
+
field_value_does_not_match_pattern: "%{field} valor não corresponde %{pattern}"
|
103
|
+
file: "Arquivo"
|
104
|
+
filters: "Filtros"
|
105
|
+
foreign_key: "Foreign key"
|
106
|
+
form: "Formulário"
|
107
|
+
form_has_been_saved: "O formulário foi salvo!"
|
108
|
+
form_has_been_submitted_successfully: "O formulário foi enviado com sucesso!"
|
109
|
+
form_name: "Nome do formulário"
|
110
|
+
forms: "Formulários"
|
111
|
+
funnel: "Funil"
|
112
|
+
general: "Geral"
|
113
|
+
greater_or_equal: "Maior ou igual"
|
114
|
+
greater_than: "Maior do que"
|
115
|
+
greater_than_or_equal_to: "Maior que ou igual a"
|
116
|
+
group_name: "Nome do grupo"
|
117
|
+
has_been_created: "foi criado"
|
118
|
+
has_been_removed_succesfully: "foi removido com sucesso"
|
119
|
+
has_been_updated: "foi atualizado"
|
120
|
+
hello_admin: "Olá Admin \U0001f44b"
|
121
|
+
hidden: "Oculto"
|
122
|
+
image: "Imagem"
|
123
|
+
includes: "Inclui"
|
124
|
+
input_type: "Tipo de entrada"
|
125
|
+
integer: "Integer"
|
126
|
+
interval: "Intervalo"
|
127
|
+
is: "É"
|
128
|
+
is_not: "Não é"
|
129
|
+
items_has_been_removed: "itens foram removidos"
|
130
|
+
items_will_be_removed: "%{count} itens serão removidos"
|
131
|
+
json: "JSON"
|
132
|
+
label: "Moeda"
|
133
|
+
less_or_equal: "Menor ou igual"
|
134
|
+
less_than: "Menor do que"
|
135
|
+
less_than_or_equal_to: "Menor ou igual a"
|
136
|
+
line_chart: "Gráfico de linhas"
|
137
|
+
link: "Link"
|
138
|
+
link_name: "Nome do link"
|
139
|
+
link_text: "Texto do link"
|
140
|
+
links: "Links"
|
141
|
+
load_initial_data: "Carregar dados iniciais"
|
142
|
+
loading: "Carregando…"
|
143
|
+
long_text: "Texto longo"
|
144
|
+
looks_like_you_are_new_here: "Parece que você é novo aqui \U0001f643"
|
145
|
+
markdown: "Markdown"
|
146
|
+
method: "Método"
|
147
|
+
method_call: "Chamada de método"
|
148
|
+
more: "Mais"
|
149
|
+
multiple: "Múltiplo"
|
150
|
+
name: "Nome"
|
151
|
+
new_alert: "Novo alerta"
|
152
|
+
new_dashboard: "Novo painel"
|
153
|
+
new_form: "Novo formulário"
|
154
|
+
new_query: "Nova consulta"
|
155
|
+
no_data: "Sem dados"
|
156
|
+
not_empty: "Não vazio"
|
157
|
+
not_found: "Não encontrado"
|
158
|
+
number: "Número"
|
159
|
+
ok: "Ok"
|
160
|
+
open_in_markdown_editor: "Abrir no editor de markdown"
|
161
|
+
options_separated_with_new_line_or_comma: "Opções separadas com nova linha ou vírgula"
|
162
|
+
or: "Ou"
|
163
|
+
other_than: "Outros que não"
|
164
|
+
param_name: "Nome do param"
|
165
|
+
password: "Senha"
|
166
|
+
path: "Caminho"
|
167
|
+
percent: "Porcento"
|
168
|
+
percentage: "Porcentagem"
|
169
|
+
pie_chart: "Gráfico de pizza"
|
170
|
+
polymorphic: "Polimórfico"
|
171
|
+
primary_key: "Chave primária"
|
172
|
+
queries: "Consultas"
|
173
|
+
queries_can_contain_variable_via_syntax_html: "As consultas podem conter variáveis incorporadas usando a sintaxe <code>{{variable}}</code>:"
|
174
|
+
query: "Consulta"
|
175
|
+
query_editor: "Editor de consultas"
|
176
|
+
query_has_been_saved: "A consulta foi salva!"
|
177
|
+
query_name: "Nome da consulta"
|
178
|
+
query_not_selected: "Consulta não selecionada"
|
179
|
+
query_revisions: "Revisões de consulta"
|
180
|
+
read_only: "Somente leitura"
|
181
|
+
read_write: "Leitura-gravação"
|
182
|
+
reference: "Referência"
|
183
|
+
reference_resource: "Recurso de referência"
|
184
|
+
remove: "Remover"
|
185
|
+
reports: "Relatórios"
|
186
|
+
request_param: "Param de solicitação"
|
187
|
+
required: "Requerido"
|
188
|
+
resource: "Recurso"
|
189
|
+
resource_filters: "%{resource} Filtros"
|
190
|
+
resource_settings: "%{resource} Configurações"
|
191
|
+
resources: "Recursos"
|
192
|
+
restore: "Restaurar"
|
193
|
+
resubmit: "Reenviar"
|
194
|
+
revert: "Reverter"
|
195
|
+
revision_has_been_applied: "A revisão foi aplicada"
|
196
|
+
revisions: "Revisões"
|
197
|
+
richtext: "Richtext"
|
198
|
+
row_chart: "Gráfico de linhas"
|
199
|
+
run: "Executar"
|
200
|
+
save: "Salvar"
|
201
|
+
save_and_create_new: "Salvar e criar novo"
|
202
|
+
save_as_new: "Salvar como novo"
|
203
|
+
save_dashboard: "Salvar painel"
|
204
|
+
save_form: "Salvar formulário"
|
205
|
+
save_query: "Salvar consulta"
|
206
|
+
scopes: "Escopos"
|
207
|
+
search: "Pesquisar"
|
208
|
+
search_placeholder: "Pesquisar..."
|
209
|
+
search_query: "Consulta de pesquisa"
|
210
|
+
select: "Selecionar"
|
211
|
+
select_alert_tags: "Selecionar tags de alerta"
|
212
|
+
select_dashboard: "Selecione o painel"
|
213
|
+
select_dashboard_tags: "Selecionar tags de painel"
|
214
|
+
select_form: "Selecionar formulário"
|
215
|
+
select_form_tags: "Selecionar tags de dashform"
|
216
|
+
select_options: "Selecionar opções"
|
217
|
+
select_placeholder: "Selecione..."
|
218
|
+
select_query: "Selecionar consulta"
|
219
|
+
select_query_tags: "Selecionar tags de consulta"
|
220
|
+
select_resource_placeholder: "Selecionar recurso..."
|
221
|
+
selected_item_has_been_removed: "O item selecionado foi removido"
|
222
|
+
selected_item_will_be_removed: "O item selecionado será removido"
|
223
|
+
selected_item_will_be_removed_are_you_sure: "O item selecionado será removido. Tem certeza?"
|
224
|
+
send_empty: "Enviar vazio?"
|
225
|
+
send_now: "Enviar agora"
|
226
|
+
send_to: "Enviar para"
|
227
|
+
set_tags: "Definir tags"
|
228
|
+
settings: "Configurações"
|
229
|
+
should_be_a_valid_json: "Deve ser um JSON válido"
|
230
|
+
should_be_error_constraint: "Deve ser %{erro} %{constraint}"
|
231
|
+
source: "Fonte"
|
232
|
+
stacked_bars: "Barras empilhadas"
|
233
|
+
starts_with: "Começa com"
|
234
|
+
submit: "Enviar"
|
235
|
+
syntax_is_used_for_if_and_if_not_conditions_html: "<code>{{#variable}}... {{/variable}}</code> e <code>{{^variable}}... A sintaxe {{variable}}</code> é usada para condições <code>if</code> e <code>se não</code>, respectivamente:"
|
236
|
+
tab_type: "Tipo de guia"
|
237
|
+
table: "Tabela"
|
238
|
+
tabs: "Guias"
|
239
|
+
tag: "Tag"
|
240
|
+
tags: "Tags"
|
241
|
+
text: "Texto"
|
242
|
+
textarea: "Área de texto"
|
243
|
+
there_are_unsaved_changes_from: "Há alterações não salvas de %{timestamp}"
|
244
|
+
through: "Através"
|
245
|
+
timezone: "Fuso horário"
|
246
|
+
title: "Título"
|
247
|
+
type: "Tipo"
|
248
|
+
unable_to_load_form_data: "Não é possível carregar dados do formulário"
|
249
|
+
unable_to_remove_item: "Não foi possível remover o item"
|
250
|
+
unable_to_remove_items: "Não foi possível remover itens"
|
251
|
+
unable_to_send_email: "Não foi possível enviar e-mail"
|
252
|
+
unable_to_submit_form: "Não foi possível enviar o formulário"
|
253
|
+
unit: "Unidade"
|
254
|
+
use_default: "Usar padrão"
|
255
|
+
value: "Valor"
|
256
|
+
values_axis: "Eixo valores"
|
257
|
+
variables: "Variáveis"
|
258
|
+
visibility: "Visibilidade"
|
259
|
+
visualization: "Visualização"
|
260
|
+
write_only: "Somente gravação"
|
261
|
+
i:
|
262
|
+
locale: pt
|
263
|
+
select:
|
264
|
+
placeholder: Selecionar
|
265
|
+
noMatch: Sem correspondências
|
266
|
+
loading: Carregando
|
267
|
+
table:
|
268
|
+
noDataText: Sem dados
|
269
|
+
noFilteredDataText: Sem dados para o filtro
|
270
|
+
confirmFilter: Aceitar
|
271
|
+
resetFilter: Resetar filtro
|
272
|
+
clearFilter: Limpar
|
273
|
+
sumText: Soma
|
274
|
+
datepicker:
|
275
|
+
selectDate: Selecionar data
|
276
|
+
selectTime: Selecionar hora
|
277
|
+
startTime: Horário de início
|
278
|
+
endTime: Horário de final
|
279
|
+
clear: Limpar
|
280
|
+
ok: Aceitar
|
281
|
+
datePanelLabel: "[mmmm] [yyyy]"
|
282
|
+
month: Mês
|
283
|
+
month1: Janeiro
|
284
|
+
month2: Fevereiro
|
285
|
+
month3: Março
|
286
|
+
month4: Abril
|
287
|
+
month5: Maio
|
288
|
+
month6: Junho
|
289
|
+
month7: Julho
|
290
|
+
month8: Agosto
|
291
|
+
month9: Setembro
|
292
|
+
month10: Outubro
|
293
|
+
month11: Novembro
|
294
|
+
month12: Dezembro
|
295
|
+
year: Ano
|
296
|
+
weekStartDay: '1'
|
297
|
+
weeks:
|
298
|
+
sun: Dom
|
299
|
+
mon: Seg
|
300
|
+
tue: Ter
|
301
|
+
wed: Qua
|
302
|
+
thu: Qui
|
303
|
+
fri: Sex
|
304
|
+
sat: Sab
|
305
|
+
months:
|
306
|
+
m1: Jan
|
307
|
+
m2: Fev
|
308
|
+
m3: Mar
|
309
|
+
m4: Abr
|
310
|
+
m5: Mai
|
311
|
+
m6: Jun
|
312
|
+
m7: Jul
|
313
|
+
m8: Ago
|
314
|
+
m9: Set
|
315
|
+
m10: Out
|
316
|
+
m11: Nov
|
317
|
+
m12: Dez
|
318
|
+
transfer:
|
319
|
+
titles:
|
320
|
+
source: Origem
|
321
|
+
target: Destino
|
322
|
+
filterPlaceholder: Buscar aqui
|
323
|
+
notFoundText: Sem resultados
|
324
|
+
modal:
|
325
|
+
okText: Aceitar
|
326
|
+
cancelText: Cancelar
|
327
|
+
poptip:
|
328
|
+
okText: Aceitar
|
329
|
+
cancelText: Cancelar
|
330
|
+
page:
|
331
|
+
prev: Página Anterior
|
332
|
+
next: Página Seguinte
|
333
|
+
total: Total
|
334
|
+
item: Elemento
|
335
|
+
items: Elementos
|
336
|
+
prev5: 5 Páginas Anteriores
|
337
|
+
next5: 5 Páginas Seguintes
|
338
|
+
page: "/page"
|
339
|
+
goto: Ir a
|
340
|
+
p: ''
|
341
|
+
rate:
|
342
|
+
star: Estrela
|
343
|
+
stars: Estrelas
|
344
|
+
tree:
|
345
|
+
emptyText: Sem Dados
|
data/config/routes.rb
CHANGED
@@ -21,7 +21,8 @@ Motor::Admin.routes.draw do
|
|
21
21
|
resources :audits, only: %i[index]
|
22
22
|
resources :resources, path: '/data/:resource',
|
23
23
|
only: %i[index show update create destroy],
|
24
|
-
controller: 'data'
|
24
|
+
controller: 'data',
|
25
|
+
constraints: { id: %r{[^/]+} } do
|
25
26
|
put '/:method', to: 'data#execute'
|
26
27
|
resources :association, path: '/:association',
|
27
28
|
only: %i[index create],
|
@@ -143,6 +143,15 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
143
143
|
add_index :motor_audits, %i[user_id user_type], name: 'motor_auditable_user_index'
|
144
144
|
add_index :motor_audits, :request_uuid
|
145
145
|
add_index :motor_audits, :created_at
|
146
|
+
|
147
|
+
model = Class.new(ApplicationRecord)
|
148
|
+
|
149
|
+
model.table_name = 'motor_configs'
|
150
|
+
|
151
|
+
model.create!(key: 'header.links', value: [{
|
152
|
+
name: '⭐ Star on GitHub',
|
153
|
+
path: 'https://github.com/omohokcoj/motor-admin'
|
154
|
+
}].to_json)
|
146
155
|
end
|
147
156
|
|
148
157
|
def self.down
|
@@ -14,5 +14,17 @@ if Rails::VERSION::MAJOR == 6
|
|
14
14
|
build_filters(arel, my_alias_tracker)
|
15
15
|
arel
|
16
16
|
end
|
17
|
+
|
18
|
+
def build_filters(manager, alias_tracker)
|
19
|
+
where_clause = nil
|
20
|
+
|
21
|
+
@filters.each do |filters|
|
22
|
+
where_clause = filter_clause_factory.build(filters, alias_tracker)
|
23
|
+
|
24
|
+
manager.where(where_clause.ast)
|
25
|
+
end
|
26
|
+
|
27
|
+
@values[:where] = where_clause if where_clause
|
28
|
+
end
|
17
29
|
end
|
18
30
|
end
|
@@ -22,9 +22,9 @@ module Motor
|
|
22
22
|
|
23
23
|
def call
|
24
24
|
models.map do |model|
|
25
|
-
Object.const_get(model.name)
|
25
|
+
model = Object.const_get(model.name)
|
26
26
|
|
27
|
-
next unless
|
27
|
+
next unless model.table_exists?
|
28
28
|
|
29
29
|
schema = build_model_schema(model)
|
30
30
|
|
@@ -37,7 +37,7 @@ module Motor
|
|
37
37
|
Rails.logger.error(e)
|
38
38
|
|
39
39
|
next
|
40
|
-
end.compact
|
40
|
+
end.compact.uniq
|
41
41
|
end
|
42
42
|
|
43
43
|
def models
|
@@ -225,8 +225,8 @@ module Motor
|
|
225
225
|
display_name: model.human_attribute_name(name),
|
226
226
|
model_name: reflection.polymorphic? ? nil : reflection.klass.name.underscore,
|
227
227
|
reference_type: reflection.belongs_to? ? 'belongs_to' : 'has_one',
|
228
|
-
foreign_key: reflection.
|
229
|
-
primary_key: reflection.polymorphic? ? 'id' : reflection.
|
228
|
+
foreign_key: reflection.join_foreign_key,
|
229
|
+
primary_key: reflection.polymorphic? ? 'id' : reflection.join_primary_key,
|
230
230
|
options: reflection.options.slice(:through, :source),
|
231
231
|
polymorphic: reflection.polymorphic?,
|
232
232
|
virtual: false
|
@@ -248,8 +248,8 @@ module Motor
|
|
248
248
|
display_name: model.human_attribute_name(name),
|
249
249
|
slug: name.underscore,
|
250
250
|
model_name: model_class.name.underscore,
|
251
|
-
foreign_key: ref.
|
252
|
-
primary_key: ref.
|
251
|
+
foreign_key: ref.join_primary_key,
|
252
|
+
primary_key: ref.join_foreign_key,
|
253
253
|
polymorphic: ref.options[:as].present?,
|
254
254
|
icon: Motor::FindIcon.call(name),
|
255
255
|
options: ref.options.slice(:through, :source),
|
@@ -290,12 +290,20 @@ module Motor
|
|
290
290
|
when ActiveModel::Validations::FormatValidator
|
291
291
|
{ format: JsRegex.new(options[:with]).to_h.slice(:source, :options) }
|
292
292
|
when ActiveRecord::Validations::LengthValidator
|
293
|
-
{ length: options }
|
293
|
+
{ length: normalize_length_validation_options(options) }
|
294
294
|
when ActiveModel::Validations::NumericalityValidator
|
295
295
|
{ numeric: options }
|
296
296
|
end
|
297
297
|
end
|
298
298
|
|
299
|
+
def normalize_length_validation_options(options)
|
300
|
+
return options if options[:in].blank?
|
301
|
+
|
302
|
+
in_range = options[:in]
|
303
|
+
|
304
|
+
options.merge(in: in_range.minmax)
|
305
|
+
end
|
306
|
+
|
299
307
|
def valid_reflection?(reflection)
|
300
308
|
reflection.klass
|
301
309
|
reflection.foreign_key
|
data/lib/motor/build_schema.rb
CHANGED
@@ -51,7 +51,11 @@ module Motor
|
|
51
51
|
COLUMN_NAME_ACCESS_TYPES = {
|
52
52
|
id: ColumnAccessTypes::READ_ONLY,
|
53
53
|
created_at: ColumnAccessTypes::READ_ONLY,
|
54
|
+
created_on: ColumnAccessTypes::READ_ONLY,
|
55
|
+
inserted_at: ColumnAccessTypes::READ_ONLY,
|
54
56
|
updated_at: ColumnAccessTypes::READ_ONLY,
|
57
|
+
updated_on: ColumnAccessTypes::READ_ONLY,
|
58
|
+
modified_at: ColumnAccessTypes::READ_ONLY,
|
55
59
|
deleted_at: ColumnAccessTypes::READ_ONLY
|
56
60
|
}.with_indifferent_access.freeze
|
57
61
|
|
@@ -12,9 +12,7 @@ module Motor
|
|
12
12
|
|
13
13
|
module_function
|
14
14
|
|
15
|
-
def call(current_user = nil, current_ability = nil)
|
16
|
-
cache_keys = LoadFromCache.load_cache_keys
|
17
|
-
|
15
|
+
def call(current_user = nil, current_ability = nil, cache_keys: LoadFromCache.load_cache_keys)
|
18
16
|
CACHE_STORE.fetch("#{I18n.locale}#{cache_keys.hash}#{current_user&.id}") do
|
19
17
|
CACHE_STORE.clear
|
20
18
|
|
@@ -23,16 +21,19 @@ module Motor
|
|
23
21
|
end
|
24
22
|
end
|
25
23
|
|
24
|
+
# rubocop:disable Metrics/AbcSize
|
26
25
|
# @return [Hash]
|
27
26
|
def build_data(cache_keys = {}, current_user = nil, current_ability = nil)
|
28
27
|
configs_cache_key = cache_keys[:configs]
|
29
28
|
|
30
29
|
{
|
30
|
+
version: Motor::VERSION,
|
31
31
|
current_user: current_user&.as_json(only: %i[id email]),
|
32
32
|
current_rules: current_ability.serialized_rules,
|
33
33
|
audits_count: Motor::Audit.count,
|
34
34
|
i18n: i18n_data,
|
35
35
|
base_path: Motor::Admin.routes.url_helpers.motor_path,
|
36
|
+
admin_settings_path: Rails.application.routes.url_helpers.try(:admin_settings_general_path),
|
36
37
|
schema: Motor::BuildSchema.call(cache_keys, current_ability),
|
37
38
|
header_links: header_links_data_hash(configs_cache_key),
|
38
39
|
homepage_layout: homepage_layout_data_hash(configs_cache_key),
|
@@ -45,6 +46,7 @@ module Motor
|
|
45
46
|
forms: forms_data_hash(build_cache_key(cache_keys, :forms, current_user, current_ability), current_ability)
|
46
47
|
}
|
47
48
|
end
|
49
|
+
# rubocop:enable Metrics/AbcSize
|
48
50
|
|
49
51
|
def i18n_data
|
50
52
|
I18n.t('motor', default: I18n.t('motor', locale: :en))
|
@@ -3,7 +3,6 @@
|
|
3
3
|
module Motor
|
4
4
|
module Configs
|
5
5
|
module SyncFromFile
|
6
|
-
FILE_PATH = Motor::Configs::FILE_PATH
|
7
6
|
MUTEXT = Mutex.new
|
8
7
|
FILE_TIMESTAMPS_STORE = ActiveSupport::Cache::MemoryStore.new(size: 1.megabyte)
|
9
8
|
|
@@ -11,7 +10,7 @@ module Motor
|
|
11
10
|
|
12
11
|
def call(with_exception: false)
|
13
12
|
MUTEXT.synchronize do
|
14
|
-
file =
|
13
|
+
file = Pathname.new(Motor::Configs.file_path)
|
15
14
|
|
16
15
|
file_timestamp =
|
17
16
|
begin
|
@@ -4,7 +4,6 @@ module Motor
|
|
4
4
|
module Configs
|
5
5
|
module WriteToFile
|
6
6
|
THREAD_POOL = Concurrent::FixedThreadPool.new(1)
|
7
|
-
FILE_PATH = Motor::Configs::FILE_PATH
|
8
7
|
|
9
8
|
module_function
|
10
9
|
|
@@ -22,7 +21,7 @@ module Motor
|
|
22
21
|
end
|
23
22
|
|
24
23
|
def write_with_lock
|
25
|
-
File.open(
|
24
|
+
File.open(Motor::Configs.file_path, 'w') do |file|
|
26
25
|
file.flock(File::LOCK_EX)
|
27
26
|
|
28
27
|
YAML.dump(Motor::Configs::BuildConfigsHash.call, file)
|
data/lib/motor/configs.rb
CHANGED
@@ -5,6 +5,19 @@ module Motor
|
|
5
5
|
FILE_PATH = 'config/motor.yml'
|
6
6
|
SYNC_API_PATH = '/motor_configs_sync'
|
7
7
|
SYNC_ACCESS_KEY = ENV.fetch('MOTOR_SYNC_API_KEY', '')
|
8
|
+
MEMFS_PATH = '/__enclose_io_memfs__/'
|
9
|
+
PWD_FILE_NAME = 'motor-admin.yml'
|
10
|
+
|
11
|
+
module_function
|
12
|
+
|
13
|
+
# @return [String]
|
14
|
+
def file_path
|
15
|
+
if Rails.root.to_s.start_with?(MEMFS_PATH)
|
16
|
+
[ENV['PWD'], PWD_FILE_NAME].join('/')
|
17
|
+
else
|
18
|
+
Rails.root.join(FILE_PATH).to_s
|
19
|
+
end
|
20
|
+
end
|
8
21
|
end
|
9
22
|
end
|
10
23
|
|
@@ -63,15 +63,15 @@ module Motor
|
|
63
63
|
result = nil
|
64
64
|
statement = prepare_sql_statement(query, limit, variables_hash, filters)
|
65
65
|
|
66
|
-
|
66
|
+
connection_class.transaction do
|
67
67
|
result =
|
68
|
-
case
|
68
|
+
case connection_class.connection.class.name
|
69
69
|
when 'ActiveRecord::ConnectionAdapters::PostgreSQLAdapter'
|
70
|
-
PostgresqlExecQuery.call(
|
70
|
+
PostgresqlExecQuery.call(connection_class.connection, statement)
|
71
71
|
else
|
72
72
|
statement = normalize_statement_for_sql(statement)
|
73
73
|
|
74
|
-
|
74
|
+
connection_class.connection.exec_query(*statement)
|
75
75
|
end
|
76
76
|
|
77
77
|
raise ActiveRecord::Rollback
|
@@ -172,6 +172,10 @@ module Motor
|
|
172
172
|
acc[variable[:name]] ||= variables_hash[variable[:name]] || variable[:default_value]
|
173
173
|
end
|
174
174
|
end
|
175
|
+
|
176
|
+
def connection_class
|
177
|
+
defined?(ResourceRecord) ? ResourceRecord : ActiveRecord::Base
|
178
|
+
end
|
175
179
|
end
|
176
180
|
end
|
177
181
|
end
|
@@ -117,7 +117,7 @@ module Motor
|
|
117
117
|
|
118
118
|
def define_belongs_to_reflection(klass, config)
|
119
119
|
klass.belongs_to(config[:name].to_sym,
|
120
|
-
class_name: config[:model_name]
|
120
|
+
class_name: config[:model_name]&.classify,
|
121
121
|
foreign_key: config[:foreign_key],
|
122
122
|
polymorphic: config[:polymorphic],
|
123
123
|
primary_key: config[:primary_key],
|
@@ -163,20 +163,29 @@ module Motor
|
|
163
163
|
|
164
164
|
def define_associations(klass, config)
|
165
165
|
config.fetch(:associations, []).each do |association|
|
166
|
-
|
166
|
+
next unless association[:virtual]
|
167
167
|
|
168
|
-
|
168
|
+
options = normalize_association_params(association)
|
169
169
|
|
170
|
-
|
171
|
-
options[:class_name] = association[:model_name].classify
|
172
|
-
options[:as] = association[:foreign_key].delete_suffix('_id') if is_polymorphic
|
170
|
+
filters = options.delete(:filters)
|
173
171
|
|
174
|
-
|
175
|
-
|
176
|
-
|
172
|
+
if filters.present?
|
173
|
+
klass.has_many(association[:name].to_sym, -> { filter(filters).tap(&:arel) }, **options.symbolize_keys)
|
174
|
+
else
|
175
|
+
klass.has_many(association[:name].to_sym, **options.symbolize_keys)
|
176
|
+
end
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
180
|
+
def normalize_association_params(params)
|
181
|
+
options = params.slice(:foreign_key, :primary_key).merge(dependent: :destroy)
|
182
|
+
|
183
|
+
options[:class_name] = params[:model_name].classify
|
184
|
+
options[:as] = params[:foreign_key].delete_suffix('_id') if params[:polymorphic]
|
185
|
+
|
186
|
+
options.merge(params[:options] || {})
|
187
|
+
end
|
188
|
+
|
180
189
|
def maybe_fetch_from_cache(model, cache_key, miss_cache_block, postprocess_block)
|
181
190
|
return miss_cache_block.call unless cache_key
|
182
191
|
|
data/lib/motor/version.rb
CHANGED
Binary file
|
Binary file
|
data/ui/dist/manifest.json
CHANGED
@@ -2601,9 +2601,9 @@
|
|
2601
2601
|
"icons/zoom-out.svg.gz": "icons/zoom-out.svg.gz",
|
2602
2602
|
"icons/zoom-question.svg": "icons/zoom-question.svg",
|
2603
2603
|
"icons/zoom-question.svg.gz": "icons/zoom-question.svg.gz",
|
2604
|
-
"main-
|
2605
|
-
"main-
|
2606
|
-
"main-
|
2607
|
-
"main.css": "main-
|
2608
|
-
"main.js": "main-
|
2604
|
+
"main-5c903ff9235e54ac03f1.css.gz": "main-5c903ff9235e54ac03f1.css.gz",
|
2605
|
+
"main-5c903ff9235e54ac03f1.js.LICENSE.txt": "main-5c903ff9235e54ac03f1.js.LICENSE.txt",
|
2606
|
+
"main-5c903ff9235e54ac03f1.js.gz": "main-5c903ff9235e54ac03f1.js.gz",
|
2607
|
+
"main.css": "main-5c903ff9235e54ac03f1.css",
|
2608
|
+
"main.js": "main-5c903ff9235e54ac03f1.js"
|
2609
2609
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motor-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pete Matsyburka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord-filter
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- app/views/motor/ui/show.html.erb
|
167
167
|
- config/locales/en.yml
|
168
168
|
- config/locales/es.yml
|
169
|
+
- config/locales/pt.yml
|
169
170
|
- config/routes.rb
|
170
171
|
- lib/generators/motor/install_generator.rb
|
171
172
|
- lib/generators/motor/migration.rb
|
@@ -1536,8 +1537,8 @@ files:
|
|
1536
1537
|
- ui/dist/icons/zoom-money.svg.gz
|
1537
1538
|
- ui/dist/icons/zoom-out.svg.gz
|
1538
1539
|
- ui/dist/icons/zoom-question.svg.gz
|
1539
|
-
- ui/dist/main-
|
1540
|
-
- ui/dist/main-
|
1540
|
+
- ui/dist/main-5c903ff9235e54ac03f1.css.gz
|
1541
|
+
- ui/dist/main-5c903ff9235e54ac03f1.js.gz
|
1541
1542
|
- ui/dist/manifest.json
|
1542
1543
|
homepage:
|
1543
1544
|
licenses:
|
Binary file
|
Binary file
|