pg_rails 7.2.3 → 7.3.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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +9 -2
  3. data/pg_associable/app/helpers/pg_associable/helpers.rb +4 -5
  4. data/pg_associable/app/javascript/modal_controller.js +80 -6
  5. data/pg_associable/spec/system/associable_spec.rb +30 -7
  6. data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +5 -0
  7. data/pg_engine/app/components/actions_component.rb +14 -0
  8. data/pg_engine/app/components/asociable_modal_component.html.slim +6 -0
  9. data/pg_engine/app/components/asociable_modal_component.rb +7 -0
  10. data/pg_engine/app/components/date_selector_component.rb +2 -2
  11. data/pg_engine/app/components/modal_component.html.slim +5 -0
  12. data/pg_engine/app/components/modal_component.rb +11 -0
  13. data/pg_engine/app/components/modal_content_component.rb +29 -0
  14. data/pg_engine/app/components/search_bar_component.html.slim +1 -1
  15. data/pg_engine/app/controllers/admin/accounts_controller.rb +1 -1
  16. data/pg_engine/app/controllers/admin/email_logs_controller.rb +1 -1
  17. data/pg_engine/app/controllers/admin/emails_controller.rb +4 -21
  18. data/pg_engine/app/controllers/admin/user_accounts_controller.rb +1 -1
  19. data/pg_engine/app/controllers/admin/users_controller.rb +1 -1
  20. data/pg_engine/app/controllers/concerns/pg_engine/resource.rb +288 -133
  21. data/pg_engine/app/controllers/pg_engine/base_controller.rb +5 -0
  22. data/pg_engine/app/controllers/public/mensaje_contactos_controller.rb +1 -1
  23. data/pg_engine/app/decorators/pg_engine/base_record_decorator.rb +36 -10
  24. data/pg_engine/app/helpers/pg_engine/flash_helper.rb +6 -2
  25. data/pg_engine/app/helpers/pg_engine/form_helper.rb +15 -2
  26. data/pg_engine/app/helpers/pg_engine/frame_helper.rb +52 -0
  27. data/pg_engine/app/lib/pg_engine/bootstrap5_breadcrumbs_builder.rb +22 -0
  28. data/pg_engine/app/lib/pg_engine/filtros_builder.rb +3 -2
  29. data/pg_engine/app/models/current.rb +1 -1
  30. data/pg_engine/app/models/email.rb +2 -0
  31. data/pg_engine/app/models/pg_engine/base_record.rb +13 -0
  32. data/pg_engine/app/views/admin/accounts/_form.html.slim +1 -4
  33. data/pg_engine/app/views/admin/email_logs/_form.html.slim +2 -5
  34. data/pg_engine/app/views/admin/emails/_form.html.slim +13 -13
  35. data/pg_engine/app/views/admin/emails/_send.html.slim +0 -1
  36. data/pg_engine/app/views/admin/emails/show.html.slim +19 -20
  37. data/pg_engine/app/views/admin/eventos/new.html.slim +0 -2
  38. data/pg_engine/app/views/admin/user_accounts/_form.html.slim +1 -4
  39. data/pg_engine/app/views/admin/users/_form.html.slim +1 -4
  40. data/pg_engine/app/views/pg_engine/base/edit.html.slim +1 -2
  41. data/pg_engine/app/views/pg_engine/base/index.html.slim +6 -3
  42. data/pg_engine/app/views/pg_engine/base/new.html.slim +1 -2
  43. data/pg_engine/app/views/public/mensaje_contactos/new.html.slim +2 -5
  44. data/pg_engine/config/initializers/kaminari.rb +3 -0
  45. data/pg_engine/config/initializers/ransack_memory.rb +13 -2
  46. data/pg_engine/config/locales/es.yml +1 -1
  47. data/pg_engine/lib/pg_engine/utils/pg_logger.rb +10 -10
  48. data/pg_engine/spec/controllers/admin/accounts_controller_spec.rb +2 -2
  49. data/pg_engine/spec/controllers/admin/email_logs_controller_spec.rb +2 -2
  50. data/pg_engine/spec/controllers/admin/emails_controller_spec.rb +2 -2
  51. data/pg_engine/spec/controllers/admin/user_accounts_controller_spec.rb +2 -2
  52. data/pg_engine/spec/controllers/admin/users_controller_spec.rb +2 -2
  53. data/pg_engine/spec/controllers/concerns/pg_engine/resource_helper_spec.rb +0 -2
  54. data/pg_engine/spec/lib/pg_engine/form_helper_spec.rb +51 -0
  55. data/pg_engine/spec/system/breadcrumbs_spec.rb +61 -0
  56. data/pg_engine/spec/system/destroy_spec.rb +1 -1
  57. data/pg_engine/spec/system/login_spec.rb +1 -1
  58. data/pg_engine/spec/system/modal_windows_spec.rb +89 -0
  59. data/pg_engine/spec/system/send_mail_spec.rb +1 -1
  60. data/pg_layout/app/javascript/application.js +38 -0
  61. data/pg_layout/app/javascript/config/turbo_rails/index.js +4 -1
  62. data/pg_layout/app/javascript/controllers/embedded_frame_controller.js +10 -0
  63. data/pg_layout/app/javascript/controllers/index.js +2 -0
  64. data/pg_layout/app/javascript/controllers/popover_toggler_controller.js +3 -2
  65. data/pg_layout/app/javascript/controllers/tooltip_controller.js +8 -0
  66. data/pg_layout/app/javascript/elements/index.js +1 -0
  67. data/pg_layout/app/javascript/elements/pg_event.js +13 -0
  68. data/pg_layout/app/views/devise/confirmations/new.html.erb +0 -1
  69. data/pg_layout/app/views/devise/passwords/edit.html.erb +0 -2
  70. data/pg_layout/app/views/devise/passwords/new.html.erb +0 -2
  71. data/pg_layout/app/views/devise/registrations/edit.html.erb +0 -2
  72. data/pg_layout/app/views/devise/registrations/new.html.erb +0 -2
  73. data/pg_layout/app/views/devise/unlocks/new.html.erb +0 -1
  74. data/pg_layout/app/views/layouts/pg_layout/base.html.slim +45 -17
  75. data/pg_layout/app/views/layouts/pg_layout/containerized.html.slim +1 -1
  76. data/pg_rails/lib/version.rb +1 -1
  77. data/pg_rails/scss/bootstrap_overrides.scss +2 -1
  78. data/pg_rails/scss/pg_rails.scss +8 -1
  79. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/controller_spec.rb +2 -2
  80. data/pg_scaffold/lib/generators/pg_scaffold/templates/controller.rb +2 -4
  81. data/pg_scaffold/lib/generators/pg_slim/templates/_form.html.slim +2 -5
  82. data/pg_scaffold/lib/generators/pg_slim/templates/show.html.slim +1 -1
  83. metadata +17 -4
  84. data/pg_associable/app/views/pg_engine/base/_pg_associable_modal.html.slim +0 -14
  85. data/pg_layout/app/views/pg_layout/_modal_show.html.slim +0 -14
@@ -1,12 +1,115 @@
1
1
  module PgEngine
2
2
  module Resource
3
+ # rubocop:disable Metrics/PerceivedComplexity
3
4
  def self.included(clazz)
4
- # clazz.before_action :authenticate_user!
5
+ class << clazz
6
+ # This is a per class variable, all subclasses of clazz inherit it
7
+ # BUT **the values are independent between all of them**
8
+ attr_accessor :nested_class, :nested_key, :clase_modelo
9
+
10
+ # TODO: separar los endpoints de la lógica de filtros para evitar esta variable
11
+ # en Agenda
12
+ attr_accessor :skip_default_breadcrumb
13
+ end
14
+ clazz.delegate :nested_key, :nested_class, :clase_modelo, to: clazz
15
+
16
+ clazz.helper_method :nested_class, :nested_key, :clase_modelo
17
+ clazz.helper_method :nested_record, :nested_id
18
+
5
19
  clazz.helper_method :atributos_para_listar
6
20
  clazz.helper_method :atributos_para_mostrar
7
21
  clazz.helper_method :current_page_size
8
22
  clazz.helper_method :show_filters?
9
23
  clazz.helper_method :available_page_sizes
24
+
25
+ clazz.before_action do
26
+ # TODO: quitar esto, que se use el attr_accessor
27
+ # o sea, quitar todas las referencias a @clase_modelo
28
+ @clase_modelo = clase_modelo
29
+ end
30
+
31
+ clazz.before_action unless: -> { clazz.skip_default_breadcrumb } do
32
+ if nested_record.present?
33
+ # Link al nested, siempre que sea no sea un embedded frame
34
+ # ya que en tal caso se supone que el nested está visible
35
+ # en el main frame. Además, si es un modal abierto desde
36
+ # el nested record, no muestro el link al mismo.
37
+ unless frame_embedded?
38
+ if modal_targeted? && referred_by?(nested_record)
39
+ add_breadcrumb nested_record.decorate.to_s_short
40
+ else
41
+ add_breadcrumb nested_record.decorate.to_s_short,
42
+ nested_record.decorate.target_object
43
+ end
44
+ end
45
+
46
+ # Texto de index pero sin link, porque se supone que es un
47
+ # embedded index o viene de tal
48
+ add_breadcrumb clase_modelo.nombre_plural
49
+
50
+ elsif !modal_targeted?
51
+ # Link al index, siempre que no sea un modal, porque en tal
52
+ # caso se supone que el index está visible en el main frame
53
+ if clase_modelo.present?
54
+ add_breadcrumb clase_modelo.nombre_plural,
55
+ url_for([pg_namespace, nested_record, clase_modelo])
56
+ else
57
+ # :nocov:
58
+ pg_warn 'clase_modelo is nil'
59
+ # :nocov:
60
+ end
61
+ end
62
+ end
63
+
64
+ clazz.layout :set_layout
65
+ end
66
+ # rubocop:enable Metrics/PerceivedComplexity
67
+
68
+ def referred_by?(object)
69
+ url_for(object.decorate.target_object) == request.referer
70
+ end
71
+
72
+ def nested_id
73
+ return unless nested_key.present? && nested_class.present?
74
+
75
+ id = params[nested_key]
76
+
77
+ # if using hashid-rails
78
+ if nested_class.respond_to? :decode_id
79
+ id = nested_class.decode_id(id)
80
+ end
81
+
82
+ id
83
+ end
84
+
85
+ def nested_record
86
+ return if nested_id.blank?
87
+
88
+ nested_class.find(nested_id)
89
+ end
90
+
91
+ def accepts_turbo_stream?
92
+ request.headers['Accept'].present? &&
93
+ request.headers['Accept'].include?('text/vnd.turbo-stream.html')
94
+ end
95
+
96
+ def respond_with_modal?
97
+ can_open_modal? || modal_targeted?
98
+ end
99
+
100
+ def can_open_modal?
101
+ request.get? &&
102
+ clase_modelo.default_modal &&
103
+ accepts_turbo_stream? &&
104
+ !in_modal?
105
+ end
106
+
107
+ def set_layout
108
+ if action_name == 'index'
109
+ 'pg_layout/base'
110
+ else
111
+ 'pg_layout/containerized'
112
+ end
10
113
  end
11
114
 
12
115
  # Public endpoints
@@ -25,18 +128,45 @@ module PgEngine
25
128
  end
26
129
 
27
130
  def show
28
- add_breadcrumb instancia_modelo.to_s_short, instancia_modelo.target_object
29
-
30
131
  pg_respond_show
31
132
  end
32
133
 
134
+ # FIXME: refactor
135
+ def respond_with_modal(component)
136
+ content = component.render_in(view_context)
137
+
138
+ if can_open_modal?
139
+ modal = ModalComponent.new.with_content(content)
140
+ render turbo_stream: turbo_stream.append_all('body', modal)
141
+ else
142
+ render html: content
143
+ end
144
+ end
145
+
33
146
  def new
34
- add_breadcrumb instancia_modelo.submit_default_value
147
+ if can_open_modal?
148
+ path = [request.path, request.query_string].compact.join('?')
149
+ component = ModalContentComponent.new(src: path)
150
+ respond_with_modal(component)
151
+ else
152
+ add_breadcrumb instancia_modelo.submit_default_value
153
+ end
35
154
  end
36
155
 
37
156
  def edit
38
- add_breadcrumb instancia_modelo.to_s_short, instancia_modelo.target_object
39
- add_breadcrumb 'Editando'
157
+ if can_open_modal?
158
+ path = [request.path, request.query_string].compact.join('?')
159
+ component = ModalContentComponent.new(src: path)
160
+ respond_with_modal(component)
161
+ else
162
+ if modal_targeted? && referred_by?(instancia_modelo)
163
+ add_breadcrumb instancia_modelo.to_s_short
164
+ else
165
+ add_breadcrumb instancia_modelo.to_s_short,
166
+ instancia_modelo.target_object
167
+ end
168
+ add_breadcrumb 'Modificando'
169
+ end
40
170
  end
41
171
 
42
172
  def create
@@ -55,36 +185,59 @@ module PgEngine
55
185
  protected
56
186
 
57
187
  def default_sort
58
- nil
188
+ 'id desc'
59
189
  end
60
190
 
61
191
  def available_page_sizes
62
- [10, 20, 30, 50, 100].push(current_page_size).uniq.sort
192
+ [5, 10, 20, 30, 50, 100].push(current_page_size).uniq.sort
193
+ end
194
+
195
+ def show_filters_by_default?
196
+ true
197
+ end
198
+
199
+ def filters_applied?
200
+ params[RansackMemory::Core.config[:param].presence || :q].present?
201
+ end
202
+
203
+ def session_key_identifier
204
+ ::RansackMemory::Core.config[:session_key_format]
205
+ .gsub('%controller_name%', controller_path.parameterize.underscore)
206
+ .gsub('%action_name%', action_name)
207
+ .gsub('%request_format%', request.format.symbol.to_s)
208
+ .gsub('%turbo_frame%', request.headers['Turbo-Frame'] || 'top')
209
+ # FIXME: rename to main?
63
210
  end
64
211
 
65
212
  def show_filters?
66
- cur_route = pg_current_route
67
- idtf = cur_route[:controller] + '#' + cur_route[:action] + '#open-filters'
213
+ return true if filters_applied?
214
+
215
+ idtf = "show-filters_#{session_key_identifier}"
68
216
 
69
217
  if params[:ocultar_filtros]
70
- session[idtf] = nil
218
+ session[idtf] = false
71
219
  elsif params[:mostrar_filtros]
72
220
  session[idtf] = true
73
221
  end
74
222
 
75
- session[idtf]
223
+ if session[idtf].nil?
224
+ show_filters_by_default?
225
+ else
226
+ session[idtf]
227
+ end
76
228
  end
77
229
 
78
230
  def current_page_size
79
- if params[:page_size].present?
80
- session[page_size_session_key] = params[:page_size].to_i
231
+ aux = params[:page_size].presence&.to_i
232
+ if aux.present? && aux.positive?
233
+ session[page_size_session_key] = aux
81
234
  end
82
235
 
83
236
  session[page_size_session_key].presence || default_page_size
84
237
  end
85
238
 
86
239
  def page_size_session_key
87
- "#{controller_name}/#{action_name}/page_size"
240
+ "page_size_#{session_key_identifier}"
88
241
  end
89
242
 
90
243
  def default_page_size
@@ -93,57 +246,52 @@ module PgEngine
93
246
 
94
247
  def pg_respond_update
95
248
  object = instancia_modelo
96
- respond_to do |format|
97
- if (@saved = object.save)
98
- format.html { redirect_to object.decorate.target_object }
99
- format.json { render json: object.decorate }
100
- else
101
- # TODO: esto solucionaría el problema?
102
- # self.instancia_modelo = instancia_modelo.decorate
103
- format.html { render :edit, status: :unprocessable_entity }
104
- format.json { render json: object.errors, status: :unprocessable_entity }
249
+ if (@saved = object.save)
250
+ respond_to do |format|
251
+ format.html do
252
+ if in_modal?
253
+ body = <<~HTML.html_safe
254
+ <pg-event data-event-name="pg:record-updated" data-turbo-temporary
255
+ data-response='#{object.decorate.to_json}'></pg-event>
256
+ HTML
257
+ render html: ModalContentComponent.new.with_content(body)
258
+ .render_in(view_context)
259
+ else
260
+ redirect_to object.decorate.target_object
261
+ end
262
+ end
263
+ format.json do
264
+ render json: object.decorate.as_json
265
+ end
105
266
  end
267
+ else
268
+ add_breadcrumb instancia_modelo.decorate.to_s_short, instancia_modelo.decorate.target_object
269
+ add_breadcrumb 'Modificando'
270
+ # TODO: esto solucionaría el problema?
271
+ # self.instancia_modelo = instancia_modelo.decorate
272
+ #
273
+ render :edit, status: :unprocessable_entity
106
274
  end
107
275
  end
108
276
 
109
277
  def pg_respond_create
110
278
  object = instancia_modelo
111
- respond_to do |format|
112
- if (@saved = object.save)
113
- # TODO: 'asociable' lo cambiaría por 'in_modal' o algo así
114
- if params[:asociable]
115
- format.turbo_stream do
116
- render turbo_stream:
117
- turbo_stream.update_all('.modal.show .pg-associable-form', <<~HTML
118
- <div data-modal-target="response" data-response='#{object.decorate.to_json}'></div>
119
- HTML
120
- )
121
- # FIXME: handlear json
122
- # render json: object.decorate, content_type: 'application/json'
123
- end
124
- end
125
- format.html do
126
- if params[:save_and_next] == 'true'
127
- new_path = "#{url_for(@clase_modelo)}/new"
128
- redirect_to new_path, notice: "#{@clase_modelo.nombre_singular} creado."
129
- else
130
- redirect_to object.decorate.target_object
131
- end
132
- end
133
- format.json { render json: object.decorate }
279
+ if (@saved = object.save)
280
+ if in_modal?
281
+ body = <<~HTML.html_safe
282
+ <pg-event data-event-name="pg:record-created" data-turbo-temporary
283
+ data-response='#{object.decorate.to_json}'></pg-event>
284
+ HTML
285
+ render turbo_stream: turbo_stream.append(current_turbo_frame, body)
134
286
  else
135
- # TODO: esto solucionaría el problema?
136
- # self.instancia_modelo = instancia_modelo.decorate
137
- if params[:asociable]
138
- format.turbo_stream do
139
- # FIXME: agregar , status: :unprocessable_entity
140
- render turbo_stream:
141
- turbo_stream.update_all('.modal.show .pg-associable-form', partial: 'form', locals: { asociable: true })
142
- end
143
- end
144
- format.html { render :new, status: :unprocessable_entity }
145
- format.json { render json: object.errors.full_messages, status: :unprocessable_entity }
287
+ redirect_to object.decorate.target_object
146
288
  end
289
+ else
290
+ add_breadcrumb instancia_modelo.decorate.submit_default_value
291
+ # TODO: esto solucionaría el problema?
292
+ # self.instancia_modelo = instancia_modelo.decorate
293
+
294
+ render :new, status: :unprocessable_entity
147
295
  end
148
296
  end
149
297
 
@@ -153,104 +301,98 @@ module PgEngine
153
301
  format.html { render_listing }
154
302
  format.xlsx do
155
303
  render xlsx: 'download',
156
- filename: "#{@clase_modelo.nombre_plural.gsub(' ', '-').downcase}" \
304
+ filename: "#{clase_modelo.nombre_plural.gsub(' ', '-').downcase}" \
157
305
  "-#{Time.zone.now.strftime('%Y-%m-%d-%H.%M.%S')}.xlsx"
158
306
  end
159
307
  end
160
308
  end
161
309
 
162
- def pg_respond_show(object = nil)
163
- object ||= instancia_modelo
164
- if params[:modal].present?
165
- respond_to do |format|
166
- format.html
167
- format.turbo_stream do
168
- render turbo_stream: turbo_stream.append_all(
169
- 'body', partial: 'pg_layout/modal_show', locals: { object: }
170
- )
171
- end
172
- end
310
+ def pg_respond_show
311
+ if can_open_modal?
312
+ path = [request.path, request.query_string].compact.join('?')
313
+ component = ModalContentComponent.new(src: path)
314
+ respond_with_modal(component)
173
315
  else
174
- respond_to do |format|
175
- format.json { render json: object }
176
- format.html
177
- end
316
+ add_breadcrumb instancia_modelo.to_s_short, instancia_modelo.target_object
178
317
  end
179
318
  end
180
319
 
320
+ def destroyed_message(model)
321
+ "#{model.model_name.human} #{model.gender == 'f' ? 'borrada' : 'borrado'}"
322
+ end
323
+
324
+ # rubocop:disable Metrics/PerceivedComplexity
181
325
  def pg_respond_destroy(model, redirect_url = nil)
182
326
  if destroy_model(model)
183
- msg = "#{model.model_name.human} #{model.gender == 'f' ? 'borrada' : 'borrado'}"
184
- respond_to do |format|
185
- if redirect_url.present?
186
- format.html do
187
- redirect_to redirect_url, notice: msg, status: :see_other
188
- end
189
- else
190
- format.turbo_stream do
191
- # Esto no es totalmente limpio pero funciona tanto en los listados como en los
192
- # modal show
193
- render turbo_stream: turbo_stream.remove(model) + turbo_stream.remove_all('.modal')
194
- end
195
- format.html do
196
- redirect_back(fallback_location: root_path, notice: msg, status: 303)
197
- end
198
- format.json { head :no_content }
199
- end
327
+ # FIXME: rename to main
328
+ if turbo_frame? && current_turbo_frame != 'top'
329
+ body = <<~HTML.html_safe
330
+ <pg-event data-event-name="pg:record-destroyed" data-turbo-temporary>
331
+ </pg-event>
332
+ HTML
333
+ render turbo_stream: turbo_stream.append(current_turbo_frame, body)
334
+ elsif redirect_url.present?
335
+ redirect_to redirect_url, notice: destroyed_message(model), status: :see_other
336
+ elsif accepts_turbo_stream?
337
+ body = <<~HTML.html_safe
338
+ <pg-event data-event-name="pg:record-destroyed" data-turbo-temporary>
339
+ </pg-event>
340
+ HTML
341
+ render turbo_stream: turbo_stream.append_all('body', body)
342
+ else
343
+ redirect_back(fallback_location: root_path,
344
+ notice: destroyed_message(model), status: 303)
200
345
  end
346
+ elsif in_modal?
347
+
348
+ flash.now[:alert] = @error_message
349
+ render turbo_stream: render_turbo_stream_flash_messages(to: '.modal-body .flash')
350
+ elsif accepts_turbo_stream?
351
+ flash.now[:alert] = @error_message
352
+ render turbo_stream: render_turbo_stream_flash_messages
201
353
  else
202
- respond_to do |format|
203
- format.html do
204
- if model.respond_to?(:associated_elements) && model.associated_elements.present?
205
- @model = model
206
- render destroy_error_details_view
207
- else
208
- flash[:alert] = @error_message
209
- redirect_back(fallback_location: root_path, status: 303)
210
- end
211
- end
212
- format.json { render json: { error: @error_message }, status: :unprocessable_entity }
213
- end
354
+ flash[:alert] = @error_message
355
+ redirect_back(fallback_location: root_path, status: 303)
214
356
  end
215
357
  end
216
-
217
- # TODO: crear esta vista en pg_rails
218
- def destroy_error_details_view
219
- 'destroy_error_details'
220
- end
358
+ # rubocop:enable Metrics/PerceivedComplexity
221
359
 
222
360
  def destroy_model(model)
223
361
  @error_message = 'No se pudo eliminar el registro'
362
+
224
363
  begin
225
364
  destroy_method = model.respond_to?(:discard) ? :discard : :destroy
226
365
  return true if model.send(destroy_method)
227
366
 
228
- @error_message = model.errors.full_messages.join(', ')
367
+ @error_message = model.errors.full_messages.join(', ').presence || @error_message
368
+
229
369
  false
230
370
  rescue ActiveRecord::InvalidForeignKey => e
231
- # class_name = /from table \"(?<table_name>[\p{L}_]*)\"/.match(e.message)[:table_name].singularize.camelcase
232
- # # pk_id = /from table \"(?<pk_id>[\p{L}_]*)\"/.match(e.message)[:pk_id].singularize.camelcase
233
- # clazz = Object.const_get class_name
234
- # objects = clazz.where(model.class.table_name.singularize => model)
235
371
  model_name = t("activerecord.models.#{model.class.name.underscore}")
236
372
  @error_message = "#{model_name} no se pudo borrar porque tiene elementos asociados."
237
- logger.debug e.message
373
+ pg_warn(e)
238
374
  end
375
+
239
376
  false
240
377
  end
241
378
 
242
379
  def render_listing
243
- @collection = @collection.page(params[:page]).per(current_page_size)
380
+ total = @collection.count
381
+ current_page = params[:page].presence&.to_i || 1
382
+ if current_page_size * (current_page - 1) > total
383
+ current_page = (total.to_f / current_page_size).ceil
384
+ end
385
+ @collection = @collection.page(current_page).per(current_page_size)
244
386
  @records_filtered = default_scope_for_current_model.any? if @collection.empty?
245
387
  end
246
388
 
247
389
  def buscar_instancia
248
- if Object.const_defined?('FriendlyId') && @clase_modelo.is_a?(FriendlyId)
249
- @clase_modelo.friendly.find(params[:id])
250
- elsif @clase_modelo.respond_to? :find_by_hashid!
251
- @clase_modelo.find_by_hashid!(params[:id])
390
+ if Object.const_defined?('FriendlyId') && clase_modelo.is_a?(FriendlyId)
391
+ clase_modelo.friendly.find(params[:id])
392
+ elsif clase_modelo.respond_to? :find_by_hashid!
393
+ clase_modelo.find_by_hashid!(params[:id])
252
394
  else
253
- @clase_modelo.find(params[:id])
395
+ clase_modelo.find(params[:id])
254
396
  end
255
397
  rescue ActiveRecord::RecordNotFound
256
398
  raise PgEngine::PageNotFoundError
@@ -258,7 +400,10 @@ module PgEngine
258
400
 
259
401
  def set_instancia_modelo
260
402
  if action_name.in? %w[new create]
261
- self.instancia_modelo = @clase_modelo.new(modelo_params)
403
+ self.instancia_modelo = clase_modelo.new(modelo_params)
404
+ if nested_id.present?
405
+ instancia_modelo.send("#{nested_key}=", nested_id)
406
+ end
262
407
  else
263
408
  self.instancia_modelo = buscar_instancia
264
409
 
@@ -291,12 +436,7 @@ module PgEngine
291
436
  end
292
437
 
293
438
  def nombre_modelo
294
- @clase_modelo.name.underscore
295
- end
296
-
297
- def clase_modelo
298
- # agarro la variable o intento con el nombre del controller
299
- @clase_modelo ||= self.class.name.singularize.gsub('Controller', '').constantize
439
+ clase_modelo.name.underscore
300
440
  end
301
441
 
302
442
  def filtros_y_policy(campos, dflt_sort = nil)
@@ -305,20 +445,35 @@ module PgEngine
305
445
  )
306
446
  scope = policy_scope(clase_modelo)
307
447
 
308
- scope = @filtros.filtrar(scope)
448
+ if nested_id.present?
449
+ scope = scope.where(nested_key => nested_id)
450
+ scope = scope.undiscarded if scope.respond_to?(:undiscarded)
451
+ elsif scope.respond_to?(:kept)
452
+ scope = scope.kept
453
+ end
454
+ # Soft deleted
309
455
 
310
456
  shared_context = Ransack::Adapters::ActiveRecord::Context.new(scope)
311
- @q = @clase_modelo.ransack(params[:q], context: shared_context)
312
-
457
+ @q = clase_modelo.ransack(params[:q], context: shared_context)
313
458
  @q.sorts = dflt_sort if @q.sorts.empty? && dflt_sort.present?
314
459
 
315
460
  shared_context.evaluate(@q)
316
461
  end
317
462
 
318
463
  def default_scope_for_current_model
319
- PgEngine::FiltrosBuilder.new(
320
- self, clase_modelo, []
321
- ).filtrar(policy_scope(clase_modelo))
464
+ scope = policy_scope(clase_modelo)
465
+
466
+ if nested_id.present?
467
+ scope = scope.where(nested_key => nested_id)
468
+
469
+ # Skip nested discarded check
470
+ scope = scope.undiscarded if scope.respond_to?(:undiscarded)
471
+ elsif scope.respond_to?(:kept)
472
+ scope = scope.kept
473
+ end
474
+ # Soft deleted, including nested discarded check
475
+
476
+ scope
322
477
  end
323
478
  end
324
479
  end
@@ -3,6 +3,10 @@
3
3
  module PgEngine
4
4
  # rubocop:disable Rails/ApplicationController
5
5
  class BaseController < ActionController::Base
6
+ before_action do
7
+ Current.controller = self
8
+ end
9
+
6
10
  # rubocop:enable Rails/ApplicationController
7
11
  include Pundit::Authorization
8
12
  include PrintHelper
@@ -10,6 +14,7 @@ module PgEngine
10
14
  include FlashHelper
11
15
  include RouteHelper
12
16
  include PgAssociable::Helpers
17
+ include FrameHelper
13
18
 
14
19
  class Redirect < PgEngine::Error
15
20
  attr_accessor :url
@@ -6,7 +6,7 @@ module Public
6
6
  class MensajeContactosController < PublicController
7
7
  include PgEngine::Resource
8
8
 
9
- before_action { @clase_modelo = MensajeContacto }
9
+ self.clase_modelo = MensajeContacto
10
10
 
11
11
  before_action(only: :index) { authorize MensajeContacto }
12
12
 
@@ -42,10 +42,14 @@ module PgEngine
42
42
  # rubocop:enable Style/MissingRespondToMissing
43
43
 
44
44
  def destroy_link_redirect
45
- destroy_link(redirect_to: helpers.url_for(target_index))
45
+ if helpers.using_modal?
46
+ destroy_link
47
+ else
48
+ destroy_link(redirect_to: helpers.url_for(target_index))
49
+ end
46
50
  end
47
51
 
48
- def destroy_link(confirm_text: '¿Estás seguro?', klass: 'btn-light', redirect_to: nil)
52
+ def destroy_link(confirm_text: '¿Confirmás que querés borrar el registro?', klass: 'btn-light', redirect_to: nil)
49
53
  return unless Pundit.policy!(Current.user, object).destroy?
50
54
 
51
55
  helpers.content_tag :span, rel: :tooltip, title: 'Eliminar' do
@@ -61,7 +65,10 @@ module PgEngine
61
65
  return unless Pundit.policy!(Current.user, object).edit?
62
66
 
63
67
  helpers.content_tag :span, rel: :tooltip, title: 'Modificar' do
64
- helpers.link_to edit_object_url, class: "btn btn-sm #{klass}" do
68
+ helpers.link_to edit_object_url,
69
+ class: "btn btn-sm #{klass}",
70
+ 'data-turbo-frame': 'modal_content',
71
+ 'data-turbo-stream': true do
65
72
  helpers.content_tag(:span, nil, class: clase_icono('pencil')) + text
66
73
  end
67
74
  end
@@ -71,7 +78,10 @@ module PgEngine
71
78
  return unless Pundit.policy!(Current.user, object).show?
72
79
 
73
80
  helpers.content_tag :span, rel: :tooltip, title: 'Ver' do
74
- helpers.link_to object_url, class: "btn btn-sm #{klass}" do
81
+ helpers.link_to object_url,
82
+ class: "btn btn-sm #{klass}",
83
+ 'data-turbo-frame': 'modal_content',
84
+ 'data-turbo-stream': true do
75
85
  helpers.content_tag(:span, nil, class: clase_icono('eye-fill')) + text
76
86
  end
77
87
  end
@@ -88,12 +98,14 @@ module PgEngine
88
98
  end
89
99
  end
90
100
 
91
- def new_link(remote: nil, klass: 'btn-warning')
101
+ def new_link(klass: 'btn-warning')
92
102
  return unless Pundit.policy!(Current.user, object).new?
93
103
 
94
104
  helpers.content_tag :span, rel: :tooltip, title: submit_default_value do
95
- helpers.link_to(new_object_url, class: "btn btn-sm #{klass}",
96
- remote:) do
105
+ helpers.link_to(new_object_url,
106
+ class: "btn btn-sm #{klass}",
107
+ 'data-turbo-frame': 'modal_content',
108
+ 'data-turbo-stream': true) do
97
109
  helpers.content_tag(:span, nil,
98
110
  class: clase_icono('plus').to_s) + "<span class='d-none d-sm-inline'> #{submit_default_value}</span>".html_safe
99
111
  end
@@ -105,15 +117,29 @@ module PgEngine
105
117
  end
106
118
 
107
119
  def new_object_url
108
- "#{helpers.url_for(target_index)}/new"
120
+ helpers.url_for(target_new)
109
121
  end
110
122
 
111
123
  def object_url
112
124
  helpers.url_for(target_object)
113
125
  end
114
126
 
127
+ def nested_record
128
+ # TODO: esto es raro
129
+ return if !Current.controller.respond_to?(:nested_record) ||
130
+ Current.controller.nested_record.nil? ||
131
+ Current.controller.nested_record.instance_of?(object.class)
132
+
133
+ Current.controller.nested_record
134
+ end
135
+
115
136
  def target_object
116
- pg_namespace.present? ? [pg_namespace, object] : object
137
+ [pg_namespace, nested_record, object].compact
138
+ end
139
+
140
+ def target_new
141
+ mod_name_sing = object.class.model_name.singular.to_sym
142
+ [:new, pg_namespace, nested_record, mod_name_sing]
117
143
  end
118
144
 
119
145
  def target_index
@@ -123,7 +149,7 @@ module PgEngine
123
149
  # actionview-7.1.3.2/lib/action_view/helpers/form_helper.rb
124
150
  def submit_default_value
125
151
  key = :create
126
- model = object.model_name.human
152
+ model = object.model_name.human.downcase
127
153
  defaults = []
128
154
  defaults << :"helpers.submit.#{object.model_name.i18n_key}.#{key}"
129
155
  defaults << :"helpers.submit.#{key}"