pg_rails 7.2.3 → 7.3.1

Sign up to get free protection for your applications and to get access to all the features.
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}"