motor-admin 0.1.102 → 0.2.1
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.
- 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
|