pg_rails 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +73 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/javascripts/pg_rails/asociacion_creable.js +85 -0
  6. data/app/assets/javascripts/pg_rails/best_in_place_datepicker.js +58 -0
  7. data/app/assets/javascripts/pg_rails/librerias.js +13 -0
  8. data/app/assets/javascripts/pg_rails/librerias_b3.js +14 -0
  9. data/app/assets/javascripts/pg_rails/validaciones.js +44 -0
  10. data/app/assets/javascripts/pg_rails.js +318 -0
  11. data/app/assets/stylesheets/pg_rails/librerias.scss +5 -0
  12. data/app/assets/stylesheets/pg_rails/pg_chosen.scss +29 -0
  13. data/app/assets/stylesheets/pg_rails/pg_rails.scss +199 -0
  14. data/app/assets/stylesheets/pg_rails_b3.scss +10 -0
  15. data/app/assets/stylesheets/pg_rails_b4.scss +12 -0
  16. data/app/assets/stylesheets/pg_rails_b5.scss +1 -0
  17. data/app/controllers/pg_rails/application_controller.rb +316 -0
  18. data/app/controllers/pg_rails/editar_en_lugar_controller.rb +24 -0
  19. data/app/decorators/pg_rails/base_decorator.rb +120 -0
  20. data/app/helpers/pg_rails/editar_en_lugar_helper.rb +106 -0
  21. data/app/helpers/pg_rails/form_helper.rb +25 -0
  22. data/app/helpers/pg_rails/postgres_helper.rb +15 -0
  23. data/app/helpers/pg_rails/print_helper.rb +176 -0
  24. data/app/inputs/pg_rails/asociacion_creable_input.rb +72 -0
  25. data/app/inputs/pg_rails/fecha_input.rb +20 -0
  26. data/app/inputs/pg_rails/selects_dependientes_input.rb +9 -0
  27. data/app/lib/pg_form_builder.rb +31 -0
  28. data/app/lib/pg_rails/filtros_builder.rb +338 -0
  29. data/app/models/pg_rails/application_record.rb +51 -0
  30. data/app/policies/pg_rails/application_policy.rb +104 -0
  31. data/app/views/application/_abrir_modal.js.erb +14 -0
  32. data/app/views/application/_actualizar_smart_listing.html.slim +3 -0
  33. data/app/views/application/_cerrar_modal.js.erb +8 -0
  34. data/app/views/application/_modal_ajax_form.js.erb +7 -0
  35. data/config/brakeman.ignore +42 -0
  36. data/config/locales/es.yml +17 -0
  37. data/config/routes.rb +3 -0
  38. data/config/spring.rb +1 -0
  39. data/lib/pg_rails/configuracion.rb +24 -0
  40. data/lib/pg_rails/core_ext.rb +17 -0
  41. data/lib/pg_rails/engine.rb +42 -0
  42. data/lib/pg_rails/simple_form/initializer.rb +583 -0
  43. data/lib/pg_rails/utils/logueador.rb +39 -0
  44. data/lib/pg_rails/version.rb +5 -0
  45. data/lib/pg_rails.rb +23 -0
  46. data/lib/tasks/auto_anotar_modelos.rake +34 -0
  47. data/lib/tasks/pg_rails_tasks.rake +5 -0
  48. metadata +89 -0
@@ -0,0 +1,29 @@
1
+
2
+ body .chosen-container-active.chosen-with-drop .chosen-single {
3
+ background: none;
4
+ }
5
+ body .chosen-container-single .chosen-single {
6
+ padding-top: 2px;
7
+ border: 1px solid #e5e6e7;
8
+ border: 1px solid #ced4da;
9
+ background: none;
10
+ box-shadow: none;
11
+ }
12
+
13
+ .chosen-container.chosen-with-drop .chosen-drop {
14
+ // margin-top: 0px;
15
+ }
16
+ .chosen-container-single a.chosen-single {
17
+ height: 36px;
18
+ line-height: 30px;
19
+ }
20
+ .chosen-container-single .chosen-single div {
21
+ padding-top: 5px;
22
+ }
23
+ .chosen-container-single .chosen-drop {
24
+ min-width: 190px;
25
+ margin-top: -3px;
26
+ }
27
+ .chosen-container.chosen-container-single .chosen-single {
28
+ border-radius: 4px;
29
+ }
@@ -0,0 +1,199 @@
1
+ // @import 'pg_rails/pg_chosen';
2
+
3
+ // Google Maps Places API
4
+ // Para que funcione en los modales
5
+ .pac-container {
6
+ z-index: 1051 !important;
7
+ }
8
+
9
+ .simple_form {
10
+ .ocultar {
11
+ input, select, textarea {
12
+ display: none;
13
+ }
14
+ }
15
+ }
16
+ .form-group.asociacion_creable {
17
+ input.oculto {
18
+ visibility: hidden;
19
+ position: absolute;
20
+ }
21
+
22
+ &:not(.completado) {
23
+ .borrar_seleccion {
24
+ display: none;
25
+ }
26
+ }
27
+ }
28
+ // oculto la columna de actions
29
+ .modal-seleccionar-asociado .listado th:last-child,
30
+ .modal-seleccionar-asociado .listado td:last-child {
31
+ display: none;
32
+ }
33
+ .modal-seleccionar-asociado .listado {
34
+ td {
35
+ cursor: pointer;
36
+ }
37
+ }
38
+ .modal-seleccionar-asociado .listado tr:hover {
39
+ background-color: lightgrey;
40
+ }
41
+
42
+ .listado {
43
+ td, th {
44
+ padding: 0.25rem 1rem;
45
+ }
46
+ }
47
+ .listado .btn-xs {
48
+ margin-right: 4px;
49
+ }
50
+ .listado .btn-sm {
51
+ min-width: 25px;
52
+ height: 25px;
53
+ padding: 1px;
54
+ margin-right: 4px;
55
+ }
56
+
57
+ .form-group.producto {
58
+ .form-control.selectize-control {
59
+ height: 36px;
60
+ // width: 200px;
61
+ }
62
+ .chosen-container {
63
+ // max-width: 200px;
64
+ }
65
+ }
66
+ // hay que agregar la clase pg-form a los forms que queremos afectar
67
+ .pg-form {
68
+ max-width: 1000px;
69
+
70
+ .form-control {
71
+ font-size: 13px;
72
+ }
73
+
74
+ .form-control.selectize-control {
75
+ height: 36px;
76
+ }
77
+ .form-group {
78
+ /*transition: height .5s ease 0s;*/
79
+ transition: opacity .3s ease 0s;
80
+ opacity: 1;
81
+ max-width: 300px;
82
+ margin-bottom: 6px;
83
+
84
+ &.datetime {
85
+ max-width: 600px;
86
+ }
87
+
88
+ &.dependent_fields {
89
+ max-width: 600px;
90
+ }
91
+
92
+ &.ocultar {
93
+ /*height: 0px;*/
94
+ opacity: 0;
95
+ }
96
+ }
97
+
98
+ .form-control.error {
99
+ border: 1px solid red;
100
+ }
101
+
102
+ label.error {
103
+ color: red;
104
+ }
105
+ }
106
+
107
+ .filter {
108
+ display: inline-block;
109
+ // margin-right: 10px;
110
+ // margin-bottom: 10px;
111
+ vertical-align: top;
112
+ min-width: 200px;
113
+
114
+ .input-group {
115
+ width: 230px;
116
+ }
117
+ }
118
+
119
+ .dependent_fields select {
120
+ // width: calc(99% / 2);
121
+ // width: 500px;
122
+ display: block;
123
+ margin-bottom: 15px;
124
+ }
125
+
126
+ .dependent_fields label {
127
+ display: block;
128
+ text-align: left!important;
129
+ }
130
+
131
+ .dependent_fields {
132
+ // max-width: 400px;
133
+ }
134
+
135
+ .btn.exportar:hover {
136
+ cursor: pointer;
137
+ }
138
+
139
+
140
+ // Editar en lugar
141
+ .editar_en_lugar_v2 {
142
+ cursor: pointer;
143
+ text-decoration: underline dotted lightgrey;
144
+ }
145
+ .editar_en_lugar_v2::after {
146
+ padding-left: 13px;
147
+ content: '✎';
148
+ opacity: 0;
149
+ }
150
+ .editar_en_lugar_v2:hover::after {
151
+ opacity: 1;
152
+ }
153
+
154
+ .editar_en_lugar_v2 form {
155
+ display: inline-block!important;
156
+ }
157
+ .editar_en_lugar_v2 input {
158
+ display: inline-block;
159
+ }
160
+ .editar_en_lugar_v2 input[type=submit] {
161
+ margin-left: 10px;
162
+ }
163
+
164
+ .pg_ajax_link {
165
+ cursor: pointer;
166
+ color: #337ab7;
167
+ text-decoration: none;
168
+ }
169
+ .pg_ajax_link:hover {
170
+ color: #23527c;
171
+ text-decoration: underline;
172
+ }
173
+
174
+ .pg-max-width-xxs {
175
+ max-width: 350px;
176
+ }
177
+ .pg-max-width-xs {
178
+ max-width: 450px;
179
+ }
180
+ .pg-max-width-sm {
181
+ max-width: 600px;
182
+ }
183
+ .pg-max-width {
184
+ max-width: 800px;
185
+ }
186
+ .pg-max-width-lg {
187
+ max-width: 1000px;
188
+ }
189
+ .pg-max-width-xl {
190
+ max-width: 1200px;
191
+ }
192
+
193
+ .pg-revert-width {
194
+ width: revert;
195
+ }
196
+
197
+ .pg-form input.date {
198
+ max-width: 140px;
199
+ }
@@ -0,0 +1,10 @@
1
+ // estilos para Bootstrap 3
2
+
3
+ @import 'pg_rails/pg_rails';
4
+
5
+ .pg-form {
6
+ .dropdown-menu > li > a {
7
+ padding: 6px 20px;
8
+ font-size: 14px;
9
+ }
10
+ }
@@ -0,0 +1,12 @@
1
+ // estilos para Bootstrap 4
2
+
3
+ @import "bootstrap-4.4.1/bootstrap";
4
+ @import 'pg_rails/pg_rails';
5
+
6
+ .tooltip { pointer-events: none; }
7
+
8
+ .form-group.asociacion_creable {
9
+ button {
10
+ font-size: 11px;
11
+ }
12
+ }
@@ -0,0 +1 @@
1
+ @import 'pg_rails/pg_rails';
@@ -0,0 +1,316 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ class ApplicationController < ActionController::Base
5
+ include ApplicationHelper
6
+ include Pundit::Authorization
7
+ # include SmartListing::Helper::ControllerExtensions
8
+ # helper SmartListing::Helper
9
+ include PrintHelper
10
+ include PostgresHelper
11
+
12
+ rescue_from PrintHelper::FechaInvalidaError, with: :fecha_invalida
13
+ rescue_from Pundit::NotAuthorizedError, with: :not_authorized
14
+
15
+ layout :setear_layout
16
+
17
+ def self.inherited(klass)
18
+ super
19
+ # incluyo los helpers de /app/helpers de la main_app
20
+ klass.helper :all
21
+ end
22
+
23
+ helper_method :mobile_device?
24
+
25
+ def mobile_device?
26
+ request.user_agent =~ /Mobile|webOS/
27
+ end
28
+
29
+ helper_method :any_filter?
30
+
31
+ def any_filter?
32
+ params.keys.reject { |a| a.in? ["controller", "action", "page", "page_size", "order_by", "order_direction"] }.any?
33
+ end
34
+
35
+
36
+ helper_method :current_page_size
37
+
38
+ def current_page_size
39
+ if params[:page_size].present?
40
+ session[:page_size] = params[:page_size]
41
+ params[:page_size].to_i
42
+ else
43
+ default_page_size
44
+ end
45
+ end
46
+
47
+ def default_page_size
48
+ session[:page_size].present? ? session[:page_size].to_i : 10
49
+ end
50
+
51
+
52
+ protected
53
+
54
+ def pg_respond_update(object: nil, extra_js: nil)
55
+ object ||= instancia_modelo
56
+ respond_to do |format|
57
+ if (@saved = object.save)
58
+ format.html do
59
+ redirect_to object.decorate.target_object
60
+ end
61
+ format.json { render json: object.decorate }
62
+ else
63
+ format.turbo_stream do
64
+ flash.now[:error] = object.errors.full_messages.join(', ')
65
+ render turbo_stream: render_turbo_stream_flash_messages
66
+ end
67
+ format.html { render :edit, status: :unprocessable_entity }
68
+ format.json { render json: object.errors, status: :unprocessable_entity }
69
+ end
70
+ format.js do
71
+ render '_cerrar_modal', locals: {
72
+ contenido: 'form', extra_js: ['modal_ajax_form', extra_js]
73
+ }
74
+ end
75
+ end
76
+ end
77
+
78
+ def pg_respond_create(object: nil, extra_js: nil)
79
+ object ||= instancia_modelo
80
+ respond_to do |format|
81
+ if (@saved = object.save)
82
+ format.html do
83
+ if params[:save_and_next] == 'true'
84
+ new_path = url_for(@clase_modelo) + "/new"
85
+ redirect_to new_path, notice: "#{@clase_modelo.nombre_singular} creado."
86
+ else
87
+ redirect_to object.decorate.target_object
88
+ end
89
+ end
90
+ format.json { render json: object.decorate }
91
+ else
92
+ format.turbo_stream do
93
+ flash.now[:error] = object.errors.full_messages.join(', ')
94
+ render turbo_stream: render_turbo_stream_flash_messages
95
+ end
96
+ format.html { render :new, status: :unprocessable_entity }
97
+ format.json { render json: object.errors.full_messages, status: :unprocessable_entity }
98
+ end
99
+ format.js do
100
+ render '_cerrar_modal', locals: {
101
+ contenido: 'form', extra_js: ['modal_ajax_form', extra_js]
102
+ }
103
+ end
104
+ end
105
+ end
106
+
107
+ def pg_respond_index(collection)
108
+ respond_to do |format|
109
+ format.json { render json: collection }
110
+ format.js { render_smart_listing }
111
+ format.html { render_smart_listing }
112
+ format.xlsx do
113
+ render xlsx: 'download',
114
+ filename: "#{@clase_modelo.nombre_plural.gsub(' ', '-').downcase}" \
115
+ "-#{Time.zone.now.strftime("%Y-%m-%d-%H.%M.%S")}.xlsx"
116
+ end
117
+ end
118
+ end
119
+
120
+ def pg_respond_show(object = nil)
121
+ respond_to do |format|
122
+ format.json { render json: object || instancia_modelo }
123
+ format.html
124
+ end
125
+ end
126
+
127
+ def pg_respond_destroy(model, redirect_url = nil)
128
+ if destroy_model(model)
129
+ respond_to do |format|
130
+ format.html do
131
+ if redirect_url.present?
132
+ redirect_to redirect_url, notice: 'Elemento borrado.', status: 303
133
+ else
134
+ redirect_back(fallback_location: root_path, notice: 'Elemento borrado.', status: 303)
135
+ end
136
+ end
137
+ format.json { head :no_content }
138
+ end
139
+ else
140
+ respond_to do |format|
141
+ format.html do
142
+ if model.respond_to?(:associated_elements) && model.associated_elements.present?
143
+ @model = model
144
+ render destroy_error_details_view
145
+ else
146
+ flash[:error] = @error_message
147
+ # if redirect_url.present?
148
+ # redirect_to redirect_url
149
+ # else
150
+ redirect_back(fallback_location: root_path, status: 303)
151
+ # end
152
+ end
153
+ end
154
+ format.json { render json: { error: @error_message }, status: :unprocessable_entity }
155
+ end
156
+ end
157
+ end
158
+
159
+ # destroy_and_respond DEPRECADO
160
+ alias_method :destroy_and_respond, :pg_respond_destroy
161
+
162
+ def destroy_error_details_view
163
+ 'destroy_error_details'
164
+ end
165
+
166
+ def destroy_model(model)
167
+ @error_message = 'No se pudo eliminar el registro'
168
+ begin
169
+ destroy_method = model.respond_to?(:discard) ? :discard : :destroy
170
+ return true if model.send(destroy_method)
171
+
172
+ @error_message = model.errors.full_messages.join(', ')
173
+ false
174
+ rescue ActiveRecord::InvalidForeignKey => e
175
+ # class_name = /from table \"(?<table_name>[\p{L}_]*)\"/.match(e.message)[:table_name].singularize.camelcase
176
+ # # pk_id = /from table \"(?<pk_id>[\p{L}_]*)\"/.match(e.message)[:pk_id].singularize.camelcase
177
+ # clazz = Object.const_get class_name
178
+ # objects = clazz.where(model.class.table_name.singularize => model)
179
+ model_name = t("activerecord.models.#{model.class.name.underscore}")
180
+ @error_message = "#{model_name} no se pudo borrar porque tiene elementos asociados."
181
+ logger.debug e.message
182
+ end
183
+ false
184
+ end
185
+
186
+ def render_smart_listing
187
+ raise 'implementar en subclase'
188
+ end
189
+
190
+ def buscar_instancia
191
+ if Object.const_defined?('FriendlyId') && @clase_modelo.is_a?(FriendlyId)
192
+ @clase_modelo.friendly.find(params[:id])
193
+ else
194
+ @clase_modelo.find(params[:id])
195
+ end
196
+ end
197
+
198
+ def set_instancia_modelo
199
+ if action_name.in? %w[new create]
200
+ self.instancia_modelo = @clase_modelo.new(modelo_params)
201
+ else
202
+ self.instancia_modelo = buscar_instancia
203
+
204
+ instancia_modelo.assign_attributes(modelo_params) if action_name.in? %w[update]
205
+ end
206
+
207
+ instancia_modelo.current_user = send(PgRails.configuracion.current_user_method)
208
+
209
+ authorize instancia_modelo
210
+
211
+ self.instancia_modelo = instancia_modelo.decorate if action_name.in? %w[show edit new]
212
+ end
213
+
214
+ def instancia_modelo=(val)
215
+ instance_variable_set("@#{nombre_modelo}".to_sym, val)
216
+ end
217
+
218
+ def instancia_modelo
219
+ instance_variable_get("@#{nombre_modelo}".to_sym)
220
+ end
221
+
222
+ def modelo_params
223
+ if action_name == 'new'
224
+ params.permit(atributos_permitidos)
225
+ else
226
+ params.require(nombre_modelo).permit(atributos_permitidos)
227
+ end
228
+ end
229
+
230
+ def nombre_modelo
231
+ @clase_modelo.name.underscore
232
+ end
233
+
234
+ def default_url_options(options = {})
235
+ if Rails.env.production?
236
+ options.merge(protocol: 'https')
237
+ else
238
+ options
239
+ end
240
+ end
241
+
242
+ def setear_layout
243
+ if params[:sin_layout] == 'true'
244
+ false
245
+ else
246
+ 'application'
247
+ end
248
+ end
249
+
250
+ def clase_modelo
251
+ # agarro la variable o intento con el nombre del controller
252
+ @clase_modelo ||= self.class.name.singularize.gsub('Controller', '').constantize
253
+ end
254
+
255
+ def filtros_y_policy(campos)
256
+ @filtros = PgRails::FiltrosBuilder.new(
257
+ self, clase_modelo, campos
258
+ )
259
+ scope = policy_scope(clase_modelo)
260
+
261
+ @filtros.filtrar(scope)
262
+ end
263
+
264
+ def do_sort(scope, field, direction)
265
+ unless scope.model.column_names.include? field.to_s
266
+ return scope
267
+ end
268
+ scope = scope.order(field => direction)
269
+ instance_variable_set(:"@field", field)
270
+ instance_variable_set(:"@direction", direction)
271
+ scope
272
+ rescue ArgumentError => e
273
+ Utils::Logueador.warning(e.to_s)
274
+ scope
275
+ end
276
+
277
+ def sort_collection(scope, options = {})
278
+ if params[:order_by].present?
279
+ field = params[:order_by]
280
+ direction = params[:order_direction]
281
+ do_sort(scope, field, direction)
282
+ elsif options[:default].present?
283
+ field = options[:default].first[0]
284
+ direction = options[:default].first[1]
285
+ do_sort(scope, field, direction)
286
+ else
287
+ scope
288
+ end
289
+ end
290
+
291
+ def fecha_invalida
292
+ respond_to do |format|
293
+ format.json do
294
+ render json: { error: 'Formato de fecha inválido' }, status: :unprocessable_entity
295
+ end
296
+ format.js { render inline: 'showToast("error", "Formato de fecha inválido")' }
297
+ format.html { go_back('Formato de fecha inválido') }
298
+ end
299
+ end
300
+
301
+ def not_authorized
302
+ respond_to do |format|
303
+ format.json do
304
+ render json: { error: 'Not authorized' }, status: :unprocessable_entity
305
+ end
306
+ # format.js { render inline: 'showToast("error", "Formato de fecha inválido")' }
307
+ format.html { go_back('Not authorized') }
308
+ end
309
+ end
310
+
311
+ def go_back(message)
312
+ flash[:error] = message
313
+ redirect_back fallback_location: root_path
314
+ end
315
+ end
316
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ class EditarEnLugarController < ::ApplicationController
5
+ def actualizar
6
+ key_modelo = params.keys[1]
7
+ modelo = Kernel.const_get(key_modelo)
8
+ objeto = modelo.find params[:id]
9
+ authorize objeto, :editar_en_lugar?
10
+ object_params = request.parameters[key_modelo]
11
+ objeto.current_user = current_user
12
+ objeto.update(object_params)
13
+ respond_with_bip(objeto, param: key_modelo)
14
+ rescue Pundit::NotAuthorizedError
15
+ objeto.errors.add(:base, 'no autorizado')
16
+ respond_with_bip(objeto)
17
+ rescue Pundit::Error => e
18
+ Rollbar.error(e)
19
+ logger.error(e.message)
20
+ objeto.errors.add(:base, e.message)
21
+ respond_with_bip(objeto)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgRails
4
+ class BaseDecorator < Draper::Decorator
5
+ include ActionView::Helpers
6
+ include PrintHelper
7
+ include FormHelper
8
+ include Pundit::Authorization
9
+
10
+ attr_accessor :output_buffer
11
+
12
+ delegate_all
13
+
14
+ def as_json(_options = {})
15
+ object.as_json.tap { |o| o[:to_s] = to_s }
16
+ end
17
+
18
+ # def method_missing(method_name, *args, &block) # rubocop:disable Style/MissingRespondToMissing
19
+ # valor = object.attributes[method_name.to_s]
20
+ # return super unless valor.present?
21
+
22
+ # if valor.instance_of?(Date)
23
+ # dmy(valor)
24
+ # # elsif valor.class == ActiveSupport::TimeWithZone
25
+ # # dmy_time(valor)
26
+ # else
27
+ # super
28
+ # end
29
+ # end
30
+
31
+ def destroy_link(message = '¿Estás seguro?')
32
+ return unless Pundit.policy!(helpers.send(PgRails.configuracion.current_user_method), object).destroy?
33
+
34
+ helpers.content_tag :span, rel: :tooltip, title: 'Eliminar' do
35
+ helpers.link_to object_url, data: { 'turbo-confirm': message, 'turbo-method': :delete },
36
+ class: "btn #{_config.clase_botones_chicos} btn-#{_config.boton_destroy}" do
37
+ helpers.content_tag :span, nil, class: clase_icono(_config.icono_destroy).to_s
38
+ end
39
+ end
40
+ end
41
+
42
+ def edit_link(texto = '')
43
+ return unless Pundit.policy!(helpers.send(PgRails.configuracion.current_user_method), object).edit?
44
+
45
+ helpers.content_tag :span, rel: :tooltip, title: 'Editar' do
46
+ helpers.link_to edit_object_url, data: { turbo_frame: :main },
47
+ class: "btn #{_config.clase_botones_chicos} btn-#{_config.boton_edit}" do
48
+ helpers.content_tag(:span, nil, class: clase_icono(_config.icono_edit).to_s) + texto
49
+ end
50
+ end
51
+ end
52
+
53
+ def show_link(texto = '')
54
+ return unless Pundit.policy!(helpers.send(PgRails.configuracion.current_user_method), object).show?
55
+
56
+ helpers.content_tag :span, rel: :tooltip, title: 'Ver' do
57
+ helpers.link_to object_url, data: { turbo_frame: :main },
58
+ class: "btn #{_config.clase_botones_chicos} btn-#{_config.boton_show}" do
59
+ helpers.content_tag(:span, nil, class: clase_icono(_config.icono_show).to_s) + texto
60
+ end
61
+ end
62
+ end
63
+
64
+ def export_link(url, texto = '')
65
+ return unless Pundit.policy!(helpers.send(PgRails.configuracion.current_user_method), object).export?
66
+
67
+ helpers.content_tag :span, rel: :tooltip, title: 'Exportar' do
68
+ helpers.content_tag :a, target: '_blank',
69
+ class: "btn #{_config.clase_botones_chicos} btn-#{_config.boton_export}", href: url_change_format(url) do
70
+ "#{helpers.content_tag(:span, nil, class: clase_icono('file-earmark-excel-fill'))} #{texto}".html_safe
71
+ end
72
+ end
73
+ end
74
+
75
+ def new_link(options = {})
76
+ return unless Pundit.policy!(helpers.send(PgRails.configuracion.current_user_method), object).new?
77
+
78
+ helpers.content_tag :span, rel: :tooltip, title: 'Crear' do
79
+ helpers.link_to new_object_url, class: "btn #{_config.clase_botones_chicos} btn-primary", remote: options[:remote] do
80
+ helpers.content_tag(:span, nil,
81
+ class: clase_icono('plus').to_s) + "<span class='d-none d-sm-inline'> Crear #{object.class.nombre_singular.downcase}</span>".html_safe
82
+ end
83
+ end
84
+ end
85
+
86
+ def edit_object_url
87
+ helpers.url_for([:edit, target_object].flatten)
88
+ end
89
+
90
+ def new_object_url
91
+ "#{helpers.url_for(target_index)}/new"
92
+ end
93
+
94
+ def object_url
95
+ helpers.url_for(target_object)
96
+ end
97
+
98
+ def target_object
99
+ default_module.present? ? [default_module, object] : object
100
+ end
101
+
102
+ def target_index
103
+ default_module.present? ? [default_module, object.class] : object.class
104
+ end
105
+
106
+ def default_module
107
+ nil
108
+ end
109
+
110
+ private
111
+
112
+ def _config
113
+ PgRails.configuracion
114
+ end
115
+
116
+ def clase_icono(icono)
117
+ "#{_config.sistema_iconos} #{_config.sistema_iconos}-#{icono}"
118
+ end
119
+ end
120
+ end