pg_rails 7.6.17 → 7.6.19

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 (56) hide show
  1. checksums.yaml +4 -4
  2. data/pg_associable/app/javascript/modal_controller.js +17 -1
  3. data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +8 -1
  4. data/pg_engine/app/components/inline_edit/inline_show_component.html.slim +2 -1
  5. data/pg_engine/app/components/search_bar_toggler_component.html.slim +5 -3
  6. data/pg_engine/app/controllers/admin/accounts_controller.rb +0 -4
  7. data/pg_engine/app/controllers/admin/email_logs_controller.rb +0 -4
  8. data/pg_engine/app/controllers/admin/emails_controller.rb +0 -4
  9. data/pg_engine/app/controllers/admin/simple_user_notifiers_controller.rb +0 -2
  10. data/pg_engine/app/controllers/admin/user_accounts_controller.rb +0 -4
  11. data/pg_engine/app/controllers/admin/users_controller.rb +0 -4
  12. data/pg_engine/app/controllers/concerns/pg_engine/resource.rb +121 -41
  13. data/pg_engine/app/controllers/public/mensaje_contactos_controller.rb +0 -4
  14. data/pg_engine/app/controllers/users/accounts_controller.rb +0 -4
  15. data/pg_engine/app/decorators/pg_engine/base_record_decorator.rb +39 -13
  16. data/pg_engine/app/helpers/pg_engine/index_helper.rb +17 -3
  17. data/pg_engine/app/helpers/pg_engine/route_helper.rb +4 -0
  18. data/pg_engine/app/lib/pg_engine/filtros_builder.rb +7 -9
  19. data/pg_engine/app/models/account.rb +2 -0
  20. data/pg_engine/app/models/concerns/pg_engine/naming.rb +5 -0
  21. data/pg_engine/app/models/pg_engine/base_record.rb +1 -4
  22. data/pg_engine/app/models/user.rb +1 -1
  23. data/pg_engine/app/policies/account_policy.rb +9 -9
  24. data/pg_engine/app/policies/pg_engine/base_policy.rb +15 -3
  25. data/pg_engine/app/views/admin/accounts/show.html.slim +0 -7
  26. data/pg_engine/app/views/admin/email_logs/show.html.slim +0 -7
  27. data/pg_engine/app/views/admin/emails/show.html.slim +0 -7
  28. data/pg_engine/app/views/admin/simple_user_notifiers/show.html.slim +0 -5
  29. data/pg_engine/app/views/admin/user_accounts/show.html.slim +0 -7
  30. data/pg_engine/app/views/admin/users/show.html.slim +1 -5
  31. data/pg_engine/app/views/pg_engine/base/index.html.slim +40 -15
  32. data/pg_engine/config/locales/es.rb +42 -0
  33. data/pg_engine/config/locales/es.yml +1 -6
  34. data/pg_engine/lib/pg_engine/route_helpers.rb +5 -0
  35. data/pg_engine/spec/controllers/admin/accounts_controller_spec.rb +4 -9
  36. data/pg_engine/spec/controllers/admin/email_logs_controller_spec.rb +4 -4
  37. data/pg_engine/spec/controllers/admin/emails_controller_spec.rb +4 -4
  38. data/pg_engine/spec/controllers/admin/user_accounts_controller_spec.rb +4 -4
  39. data/pg_engine/spec/controllers/admin/users_controller_spec.rb +4 -9
  40. data/pg_engine/spec/requests/resource_spec.rb +84 -0
  41. data/pg_engine/spec/requests/users/switcher_spec.rb +2 -2
  42. data/pg_engine/spec/system/breadcrumbs_spec.rb +9 -21
  43. data/pg_engine/spec/system/destroy_spec.rb +3 -3
  44. data/pg_engine/spec/system/login_spec.rb +1 -1
  45. data/pg_engine/spec/system/modal_windows_spec.rb +5 -5
  46. data/pg_engine/spec/system/tenants_spec.rb +2 -2
  47. data/pg_layout/app/javascript/application.js +4 -2
  48. data/pg_layout/app/javascript/controllers/embedded_frame_controller.js +4 -0
  49. data/pg_layout/app/views/layouts/pg_layout/base.html.slim +3 -1
  50. data/pg_layout/app/views/layouts/pg_layout/show.html.slim +10 -0
  51. data/pg_rails/lib/version.rb +1 -1
  52. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/controller_spec.rb +4 -11
  53. data/pg_scaffold/lib/generators/pg_scaffold/templates/controller.rb +1 -5
  54. data/pg_scaffold/lib/generators/pg_slim/templates/show.html.slim +0 -7
  55. metadata +5 -3
  56. data/pg_engine/app/components/actions_component.rb +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a05ebc7aa2578ab2c32a72e68dc56b958aa0787216c6703764af4d906eba4aff
4
- data.tar.gz: 43e3d8a43d5d4b6b490de9aec4f1b122051905c81c40f699868e2015b2db314f
3
+ metadata.gz: e3c2a1c474c0e93c88c3403a1c029908571afa71299d67bb61333f1dc41df4d6
4
+ data.tar.gz: 5bc01cc28aac8510bdac8d9e2b8b50f68c60d047d70dc0e47c693211dc4462a7
5
5
  SHA512:
6
- metadata.gz: 2c9d7e326b078d50d6b5e7b8ac04f8aecfbe6217d6eb5d6984139cf1a9a9ab4f8f06e423e2b2babbfbd51266100bdd6191960a79d4b7a9a2c351f8647d3493a4
7
- data.tar.gz: dea6b0686fe63c661a129b17a880d4158fec6fcc0430e96aaaace4444fddffd84818dabd164dee2523906824babded554cb0a10c49ed419da73b8d2c4a44baff
6
+ metadata.gz: af345cd05556af2813a3c492f4d8d76565f87eb34f7607a197ce9509ac35f4fbaea46029a9053f0768add6185d9940246360cf6c74705c0d88938c28e9b0fcce
7
+ data.tar.gz: f357484dbc3a12c00332574f8b7389020193a0a456b2ec0b1af8b1cd340c3a0c2855eea40ba8df125720283ddea2f741db8a45c0b8c3588348b5481342b8c6a9
@@ -1,5 +1,6 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
  import * as bootstrap from 'bootstrap'
3
+ import { Rollbar } from 'rollbar'
3
4
 
4
5
  export default class extends Controller {
5
6
  static outlets = ['asociable']
@@ -41,7 +42,13 @@ export default class extends Controller {
41
42
  this.reloadTop()
42
43
  ev.stopPropagation()
43
44
  } else {
44
- this.back(ev)
45
+ if (ev.data.dataset.reload) {
46
+ this.reload()
47
+ this.reloadTop()
48
+ ev.stopPropagation()
49
+ } else {
50
+ this.back(ev)
51
+ }
45
52
  }
46
53
  })
47
54
 
@@ -73,6 +80,15 @@ export default class extends Controller {
73
80
  }
74
81
  }
75
82
 
83
+ reload () {
84
+ const frame = this.element.querySelector('turbo-frame')
85
+ if (frame.attributes.src) {
86
+ frame.reload()
87
+ } else {
88
+ Rollbar.error('Modal without frame')
89
+ }
90
+ }
91
+
76
92
  reloadTop () {
77
93
  // FIXME: rename to main?
78
94
  const topFrame = document.querySelector('#top')
@@ -71,11 +71,18 @@ input[type=datetime-local], input[type=datetime] {
71
71
  .listado {
72
72
  td, th {
73
73
  padding: 0.25rem 1rem;
74
+
75
+ &:has(.actions-wrapper) {
76
+ width: 0.1%;
77
+ border-right: 1px solid #c1c1c1;
78
+ padding: 0 1rem;
79
+ vertical-align: middle;
80
+ }
74
81
  }
75
82
  }
76
83
  .listado .actions-wrapper {
77
84
  display: flex;
78
- justify-content: flex-end;
85
+ // justify-content: flex-end;
79
86
  gap: 4px;
80
87
 
81
88
  .btn-sm {
@@ -1,4 +1,5 @@
1
- - if @model.class.inline_editable?(@unsuffixed_attribute) && helpers.policy(@model).edit?
1
+ - if Current.namespace == :users && \
2
+ @model.class.inline_editable?(@unsuffixed_attribute) && helpers.policy(@model).edit?
2
3
  = helpers.turbo_frame_tag(@frame_id, class: 'inline-edit')
3
4
  - if @record_updated
4
5
  pg-event[data-event-name="pg:record-updated" data-inline="true" data-turbo-temporary=""
@@ -1,8 +1,10 @@
1
1
  - if helpers.show_filters?
2
2
  = link_to url_for(ocultar_filtros: 1, cancel_filter: 'true'),
3
- class: 'btn btn-sm btn-outline-primary col-auto' do
3
+ class: 'dropdown-item icon-link' do
4
+ i.bi.bi-funnel-fill.lh-1
4
5
  | Ocultar filtros
5
6
  - else
6
7
  = link_to url_for(mostrar_filtros: 1),
7
- class: 'btn btn-sm btn-outline-primary col-auto' do
8
- | Filtrar
8
+ class: 'dropdown-item icon-link' do
9
+ i.bi.bi-funnel-fill.lh-1
10
+ | Mostrar filtros
@@ -8,10 +8,6 @@ module Admin
8
8
 
9
9
  self.clase_modelo = Account
10
10
 
11
- before_action(only: :index) { authorize Account }
12
-
13
- before_action :set_instancia_modelo, only: %i[new create show edit update destroy]
14
-
15
11
  private
16
12
 
17
13
  def atributos_permitidos
@@ -8,10 +8,6 @@ module Admin
8
8
 
9
9
  self.clase_modelo = EmailLog
10
10
 
11
- before_action(only: :index) { authorize EmailLog }
12
-
13
- before_action :set_instancia_modelo, only: %i[new create show edit update destroy]
14
-
15
11
  before_action do
16
12
  @actions = [
17
13
  ["Mailgun sync: #{ENV.fetch('MAILGUN_DOMAIN', nil)}", mailgun_sync_admin_email_logs_path, {
@@ -8,10 +8,6 @@ module Admin
8
8
 
9
9
  self.clase_modelo = Email
10
10
 
11
- before_action(only: :index) { authorize Email }
12
-
13
- before_action :set_instancia_modelo, only: %i[new create show edit update destroy]
14
-
15
11
  after_action only: :create do
16
12
  if @saved
17
13
  PgEngine::AdminMailer.with(email_object: @email).admin_mail.deliver_later
@@ -4,8 +4,6 @@ module Admin
4
4
 
5
5
  self.clase_modelo = SimpleUserNotifier
6
6
 
7
- before_action :set_instancia_modelo, only: %i[new create show edit update destroy]
8
-
9
7
  def column_options_for(object, attribute)
10
8
  case attribute
11
9
  when :message
@@ -10,10 +10,6 @@ module Admin
10
10
  self.nested_class = Account
11
11
  self.nested_key = :account_id
12
12
 
13
- before_action(only: :index) { authorize UserAccount }
14
-
15
- before_action :set_instancia_modelo, only: %i[new create show edit update destroy]
16
-
17
13
  private
18
14
 
19
15
  def atributos_permitidos
@@ -8,14 +8,10 @@ module Admin
8
8
 
9
9
  self.clase_modelo = User
10
10
 
11
- before_action(only: :index) { authorize User }
12
-
13
11
  before_action only: %i[update] do
14
12
  params[:user].delete(:password) if params[:user][:password].blank?
15
13
  end
16
14
 
17
- before_action :set_instancia_modelo, only: %i[new create show edit update destroy]
18
-
19
15
  def create
20
16
  @user.skip_confirmation!
21
17
  @user.orphan = true
@@ -10,6 +10,9 @@ module PgEngine
10
10
  # TODO: separar los endpoints de la lógica de filtros para evitar esta variable
11
11
  # en Agenda
12
12
  attr_accessor :skip_default_breadcrumb
13
+
14
+ # Skip set_instancia_modelo && authorize
15
+ attr_accessor :skip_default_hooks
13
16
  end
14
17
  clazz.delegate :nested_key, :nested_class, :clase_modelo, to: clazz
15
18
 
@@ -22,6 +25,7 @@ module PgEngine
22
25
  clazz.helper_method :show_filters?
23
26
  clazz.helper_method :available_page_sizes
24
27
  clazz.helper_method :column_options_for
28
+ clazz.helper_method :instancia_modelo
25
29
 
26
30
  clazz.before_action do
27
31
  # TODO: quitar esto, que se use el attr_accessor
@@ -29,6 +33,14 @@ module PgEngine
29
33
  @clase_modelo = clase_modelo
30
34
  end
31
35
 
36
+ clazz.before_action(only: %i[index archived], unless: -> { clazz.skip_default_hooks }) do
37
+ authorize clase_modelo
38
+ end
39
+
40
+ clazz.before_action :set_instancia_modelo,
41
+ only: %i[new create show edit update destroy archive restore],
42
+ unless: -> { clazz.skip_default_hooks }
43
+
32
44
  clazz.before_action unless: -> { clazz.skip_default_breadcrumb } do
33
45
  if nested_record.present?
34
46
  # Link al nested, siempre que sea no sea un embedded frame
@@ -40,20 +52,28 @@ module PgEngine
40
52
  add_breadcrumb nested_record.decorate.to_s_short
41
53
  else
42
54
  add_breadcrumb nested_record.decorate.to_s_short,
43
- nested_record.decorate.target_object
55
+ path_for(nested_record.decorate.target_object)
44
56
  end
45
57
  end
46
58
 
47
- # Texto de index pero sin link, porque se supone que es un
48
- # embedded index o viene de tal
49
- add_breadcrumb clase_modelo.nombre_plural
59
+ if action_name == 'archived'
60
+ # En el listado de archivados tiene que haber un link al listado
61
+ # principal
62
+ add_breadcrumb clase_modelo.nombre_plural,
63
+ path_for([pg_namespace, nested_record, clase_modelo])
64
+ else
65
+ # Texto de index pero sin link, porque se supone que es un
66
+ # embedded index o es un show dentro de un embedded y por ende
67
+ # el paso atrás no tiene que ser el listado sino el show del parent
68
+ add_breadcrumb clase_modelo.nombre_plural
69
+ end
50
70
 
51
71
  elsif !modal_targeted?
52
72
  # Link al index, siempre que no sea un modal, porque en tal
53
73
  # caso se supone que el index está visible en el main frame
54
74
  if clase_modelo.present?
55
75
  add_breadcrumb clase_modelo.nombre_plural,
56
- url_for([pg_namespace, nested_record, clase_modelo])
76
+ path_for([pg_namespace, nested_record, clase_modelo])
57
77
  else
58
78
  # :nocov:
59
79
  pg_warn 'clase_modelo is nil'
@@ -113,8 +133,10 @@ module PgEngine
113
133
  end
114
134
 
115
135
  def set_layout
116
- if action_name == 'index'
136
+ if action_name.in? %w[index archived]
117
137
  'pg_layout/base'
138
+ elsif action_name == 'show'
139
+ 'pg_layout/show'
118
140
  else
119
141
  'pg_layout/containerized'
120
142
  end
@@ -129,10 +151,20 @@ module PgEngine
129
151
  pg_respond_buscar
130
152
  end
131
153
 
154
+ def archived
155
+ add_breadcrumb 'Archivados'
156
+
157
+ @index_url = index_url
158
+
159
+ @collection = filtros_y_policy(atributos_para_buscar, 'discarded_at desc', archived: true)
160
+
161
+ pg_respond_index(archived: true)
162
+ end
163
+
132
164
  def index
133
165
  @collection = filtros_y_policy(atributos_para_buscar, default_sort)
134
166
 
135
- pg_respond_index
167
+ pg_respond_index(archived: false)
136
168
  end
137
169
 
138
170
  def show
@@ -186,12 +218,48 @@ module PgEngine
186
218
  end
187
219
 
188
220
  def destroy
189
- pg_respond_destroy(instancia_modelo, params[:redirect_to])
221
+ pg_respond_destroy(instancia_modelo, params[:land_on])
222
+ end
223
+
224
+ def archive
225
+ discard_undiscard(:discard)
226
+ end
227
+
228
+ def restore
229
+ discard_undiscard(:undiscard)
190
230
  end
191
231
  # End public endpoints
192
232
 
193
233
  protected
194
234
 
235
+ def discard_undiscard(method)
236
+ if instancia_modelo.send(method)
237
+ if accepts_turbo_stream?
238
+ body = <<~HTML.html_safe
239
+ <pg-event data-event-name="pg:record-updated" data-reload="true" data-turbo-temporary>
240
+ </pg-event>
241
+ HTML
242
+ render turbo_stream: turbo_stream.append(current_turbo_frame, body)
243
+ else
244
+ redirect_to instancia_modelo.decorate.target_object
245
+ end
246
+
247
+ else
248
+ message = I18n.t('pg_engine.resource_not_updated', model: instancia_modelo.class)
249
+ if accepts_turbo_stream?
250
+ flash.now[:alert] = message
251
+ render turbo_stream: render_turbo_stream_flash_messages, status: :unprocessable_entity
252
+ else
253
+ flash[:alert] = message
254
+ redirect_to instancia_modelo.decorate.target_object
255
+ end
256
+ end
257
+ end
258
+
259
+ def index_url
260
+ path_for([pg_namespace, nested_record, clase_modelo])
261
+ end
262
+
195
263
  def default_sort
196
264
  'id desc'
197
265
  end
@@ -309,10 +377,10 @@ module PgEngine
309
377
  end
310
378
  end
311
379
 
312
- def pg_respond_index
380
+ def pg_respond_index(archived:)
313
381
  respond_to do |format|
314
382
  format.json { render json: @collection }
315
- format.html { render_listing }
383
+ format.html { render_listing(archived:) }
316
384
  format.xlsx do
317
385
  render xlsx: 'download',
318
386
  filename: "#{clase_modelo.nombre_plural.gsub(' ', '-').downcase}" \
@@ -331,12 +399,20 @@ module PgEngine
331
399
  end
332
400
  end
333
401
 
334
- def destroyed_message(model)
335
- "#{model.model_name.human} #{model.gender == 'f' ? 'borrada' : 'borrado'}"
402
+ def land_on_url(land_on)
403
+ case land_on
404
+ when 'index'
405
+ index_url
406
+ else
407
+ # :nocov:
408
+ pg_warn "Unrecognized land_on: #{land_on}"
409
+ instancia_modelo.decorate.target_object
410
+ # :nocov:
411
+ end
336
412
  end
337
413
 
338
414
  # rubocop:disable Metrics/PerceivedComplexity
339
- def pg_respond_destroy(model, redirect_url = nil)
415
+ def pg_respond_destroy(model, land_on = nil)
340
416
  if destroy_model(model)
341
417
  # FIXME: rename to main
342
418
  if turbo_frame? && current_turbo_frame != 'top'
@@ -345,8 +421,10 @@ module PgEngine
345
421
  </pg-event>
346
422
  HTML
347
423
  render turbo_stream: turbo_stream.append(current_turbo_frame, body)
348
- elsif redirect_url.present?
349
- redirect_to redirect_url, notice: destroyed_message(model), status: :see_other
424
+ elsif land_on.present?
425
+ redirect_to land_on_url(land_on),
426
+ notice: I18n.t('pg_engine.resource_destroyed', model: model.class),
427
+ status: :see_other
350
428
  elsif accepts_turbo_stream?
351
429
  body = <<~HTML.html_safe
352
430
  <pg-event data-event-name="pg:record-destroyed" data-turbo-temporary>
@@ -355,7 +433,8 @@ module PgEngine
355
433
  render turbo_stream: turbo_stream.append_all('body', body)
356
434
  else
357
435
  redirect_back(fallback_location: root_path,
358
- notice: destroyed_message(model), status: 303)
436
+ notice: I18n.t('pg_engine.resource_destroyed', model: model.class),
437
+ status: 303)
359
438
  end
360
439
  elsif in_modal?
361
440
 
@@ -372,32 +451,31 @@ module PgEngine
372
451
  # rubocop:enable Metrics/PerceivedComplexity
373
452
 
374
453
  def destroy_model(model)
375
- @error_message = 'No se pudo eliminar el registro'
454
+ @error_message = I18n.t('pg_engine.resource_not_destroyed', model: model.class)
376
455
 
377
456
  begin
378
- destroy_method = model.respond_to?(:discard) ? :discard : :destroy
379
- return true if model.send(destroy_method)
457
+ return true if model.destroy
380
458
 
381
459
  @error_message = model.errors.full_messages.join(', ').presence || @error_message
382
460
 
383
461
  false
384
462
  rescue ActiveRecord::InvalidForeignKey => e
385
- model_name = t("activerecord.models.#{model.class.name.underscore}")
386
- @error_message = "#{model_name} no se pudo borrar porque tiene elementos asociados."
387
463
  pg_warn(e)
464
+ @error_message = I18n.t('pg_engine.resource_not_destroyed_because_associated', model: model.class)
388
465
  end
389
466
 
390
467
  false
391
468
  end
392
469
 
393
- def render_listing
470
+ def render_listing(archived:)
394
471
  total = @collection.count
395
472
  current_page = params[:page].presence&.to_i || 1
396
473
  if current_page_size * (current_page - 1) >= total
397
474
  current_page = (total.to_f / current_page_size).ceil
398
475
  end
399
476
  @collection = @collection.page(current_page).per(current_page_size)
400
- @records_filtered = default_scope_for_current_model.any? if @collection.empty?
477
+ @records_filtered = default_scope_for_current_model(archived:).any? if @filtros.present? && @collection.empty?
478
+ render :index
401
479
  end
402
480
 
403
481
  def buscar_instancia
@@ -450,19 +528,11 @@ module PgEngine
450
528
  clase_modelo.name.underscore
451
529
  end
452
530
 
453
- def filtros_y_policy(campos, dflt_sort = nil)
531
+ def filtros_y_policy(campos, dflt_sort = nil, archived: false)
454
532
  @filtros = PgEngine::FiltrosBuilder.new(
455
533
  self, clase_modelo, campos
456
534
  )
457
- scope = policy_scope(clase_modelo)
458
-
459
- if nested_id.present?
460
- scope = scope.where(nested_key => nested_id)
461
- scope = scope.undiscarded if scope.respond_to?(:undiscarded)
462
- elsif scope.respond_to?(:kept)
463
- scope = scope.kept
464
- end
465
- # Soft deleted
535
+ scope = default_scope_for_current_model(archived:)
466
536
 
467
537
  shared_context = Ransack::Adapters::ActiveRecord::Context.new(scope)
468
538
  @q = clase_modelo.ransack(params[:q], context: shared_context)
@@ -471,20 +541,30 @@ module PgEngine
471
541
  shared_context.evaluate(@q)
472
542
  end
473
543
 
474
- def default_scope_for_current_model
544
+ def soft_delete_filter(scope, archived:)
545
+ return scope unless scope.respond_to?(:discarded)
546
+
547
+ if nested_id.present?
548
+ if archived
549
+ scope.discarded
550
+ else
551
+ scope.undiscarded
552
+ end
553
+ elsif archived
554
+ scope.unkept
555
+ else
556
+ scope.kept
557
+ end
558
+ end
559
+
560
+ def default_scope_for_current_model(archived: false)
475
561
  scope = policy_scope(clase_modelo)
476
562
 
477
563
  if nested_id.present?
478
564
  scope = scope.where(nested_key => nested_id)
479
-
480
- # Skip nested discarded check
481
- scope = scope.undiscarded if scope.respond_to?(:undiscarded)
482
- elsif scope.respond_to?(:kept)
483
- scope = scope.kept
484
565
  end
485
- # Soft deleted, including nested discarded check
486
566
 
487
- scope
567
+ soft_delete_filter(scope, archived:)
488
568
  end
489
569
  end
490
570
  end
@@ -18,10 +18,6 @@ module Public
18
18
 
19
19
  self.clase_modelo = MensajeContacto
20
20
 
21
- before_action(only: :index) { authorize MensajeContacto }
22
-
23
- before_action :set_instancia_modelo, only: %i[new create]
24
-
25
21
  layout 'pg_layout/container_logo'
26
22
 
27
23
  def new; end
@@ -10,10 +10,6 @@ module Users
10
10
  add_breadcrumb 'Cuentas'
11
11
  self.skip_default_breadcrumb = true
12
12
 
13
- before_action(only: :index) { authorize Account }
14
-
15
- before_action :set_instancia_modelo, only: %i[new create show edit update destroy]
16
-
17
13
  # private
18
14
 
19
15
  # def atributos_permitidos
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
3
4
  module PgEngine
4
5
  class BaseRecordDecorator < Draper::Decorator
5
6
  include ActionView::Helpers
@@ -28,8 +29,7 @@ module PgEngine
28
29
 
29
30
  def text_for_new
30
31
  klass = object.class
31
- # TODO: no está bueno tener que hacer el new, gender debería estar en la clase
32
- verb = klass.new.gender == 'f' ? 'Nueva' : 'Nuevo'
32
+ verb = klass.gender == 'f' ? 'Nueva' : 'Nuevo'
33
33
  "#{verb} #{klass.model_name.human.downcase}"
34
34
  end
35
35
 
@@ -57,22 +57,50 @@ module PgEngine
57
57
  if helpers.using_modal?
58
58
  destroy_link
59
59
  else
60
- destroy_link(redirect_to: helpers.url_for(target_index))
60
+ destroy_link(land_on: :index)
61
61
  end
62
62
  end
63
63
 
64
- def destroy_link(confirm_text: '¿Confirmás que querés borrar el registro?', klass: 'btn-light', redirect_to: nil)
64
+ def destroy_link(confirm_text: '¿Confirmás que querés borrar el registro?', klass: 'btn-light', land_on: nil)
65
65
  return unless Pundit.policy!(Current.user, object).destroy?
66
66
 
67
- helpers.content_tag :span, rel: :tooltip, title: 'Eliminar' do
68
- helpers.link_to object_url + (redirect_to.present? ? "?redirect_to=#{redirect_to}" : ''),
67
+ helpers.content_tag :span, rel: :tooltip, title: 'Eliminar definitivamente' do
68
+ helpers.link_to object_url + (land_on.present? ? "?land_on=#{land_on}" : ''),
69
69
  data: { 'turbo-confirm': confirm_text, 'turbo-method': :delete },
70
- class: "btn btn-sm #{klass}" do
70
+ class: "btn btn-sm #{klass} text-danger" do
71
71
  helpers.content_tag :span, nil, class: clase_icono('trash-fill')
72
72
  end
73
73
  end
74
74
  end
75
75
 
76
+ def archive_link(klass: 'btn-light')
77
+ return unless Pundit.policy!(Current.user, object).archive?
78
+
79
+ target_archive = [:archive, pg_namespace, nested_record, object]
80
+
81
+ helpers.content_tag :span, rel: :tooltip, title: 'Archivar' do
82
+ helpers.link_to helpers.url_for(target_archive),
83
+ data: { 'turbo-method': :post, 'turbo-stream': true },
84
+ class: "btn btn-sm #{klass}" do
85
+ helpers.content_tag :span, nil, class: clase_icono('archive-fill')
86
+ end
87
+ end
88
+ end
89
+
90
+ def restore_link(klass: 'btn-light')
91
+ return unless Pundit.policy!(Current.user, object).restore?
92
+
93
+ target_archive = [:restore, pg_namespace, nested_record, object]
94
+
95
+ helpers.content_tag :span, rel: :tooltip, title: 'Desarchivar' do
96
+ helpers.link_to helpers.url_for(target_archive),
97
+ data: { 'turbo-method': :post, 'turbo-stream': true },
98
+ class: "btn btn-sm #{klass}" do
99
+ helpers.content_tag(:span, nil, class: clase_icono('arrow-counterclockwise')) + ' Desarchivar'
100
+ end
101
+ end
102
+ end
103
+
76
104
  def edit_link(text: ' Modificar', klass: 'btn-warning')
77
105
  return unless Pundit.policy!(Current.user, object).edit?
78
106
 
@@ -103,14 +131,11 @@ module PgEngine
103
131
  end
104
132
  end
105
133
 
106
- def export_link(url, text: '', klass: 'btn-info')
134
+ def export_link(url, text: '')
107
135
  return unless Pundit.policy!(Current.user, object).export?
108
136
 
109
- helpers.content_tag :span, rel: :tooltip, title: 'Exportar en excel' do
110
- helpers.content_tag :a, target: '_blank',
111
- class: "btn btn-sm #{klass}", href: url_change_format(url, 'xlsx') do
112
- "#{helpers.content_tag(:span, nil, class: clase_icono('file-earmark-excel-fill'))} #{text}".html_safe
113
- end
137
+ helpers.content_tag :a, target: '_blank', class: 'icon-link dropdown-item lh-1', href: url_change_format(url, 'xlsx') do
138
+ "#{helpers.content_tag(:span, nil, class: clase_icono('file-earmark-excel-fill'))} #{text}".html_safe
114
139
  end
115
140
  end
116
141
 
@@ -182,3 +207,4 @@ module PgEngine
182
207
  end
183
208
  end
184
209
  end
210
+ # rubocop:enable Metrics/ClassLength
@@ -18,13 +18,13 @@ module PgEngine
18
18
  sort_field = sort_field.to_s.sub(/_text\z/, '')
19
19
  end
20
20
 
21
+ # Unsuffixed
21
22
  campo = campo.to_s.sub(/_f\z/, '')
22
23
  campo = campo.to_s.sub(/_text\z/, '')
23
24
 
24
25
  clase = options[:clase] || @clase_modelo
25
- key = [controller_name, action_name, 'listado_header', campo].join('.')
26
- dflt = :"activerecord.attributes.#{clase.model_name.i18n_key}.#{campo}"
27
- human_name = clase.human_attribute_name(key, default: dflt)
26
+
27
+ human_name = scoped_human_attr_name(clase, campo, 'listado_header')
28
28
 
29
29
  if options[:ordenable]
30
30
  if sort_field.is_a? Array
@@ -37,6 +37,20 @@ module PgEngine
37
37
  end
38
38
  end
39
39
 
40
+ def scoped_human_attr_name(clase, campo, scope)
41
+ action_key = build_scoped_key(clase, campo, scope, action_name)
42
+ scope_key = build_scoped_key(clase, campo, scope)
43
+
44
+ I18n.t(action_key, default: [scope_key, clase.human_attribute_name(campo)])
45
+ end
46
+
47
+ def build_scoped_key(clase, campo, scope = nil, subscope = nil)
48
+ campo = "#{campo}/scoped" if scope.present?
49
+ scope = "#{subscope}/#{scope}" if subscope.present?
50
+
51
+ ['activerecord.attributes', clase.model_name.i18n_key, campo, scope].compact.join('.').to_sym
52
+ end
53
+
40
54
  def default_order(campo)
41
55
  columna = @clase_modelo.columns.find { _1.name == campo.to_s }
42
56
  if columna && columna.type.to_s.include?('date')
@@ -63,5 +63,9 @@ module PgEngine
63
63
 
64
64
  polymorphic_url(target.flatten.compact, options.merge(only_path: true))
65
65
  end
66
+
67
+ def path_for(array)
68
+ url_for(array.push({ only_path: true }))
69
+ end
66
70
  end
67
71
  end
@@ -5,6 +5,7 @@ module PgEngine
5
5
  include ActionView::Helpers
6
6
  include ActionView::Context
7
7
  include PostgresHelper
8
+ include IndexHelper
8
9
  attr_accessor :controller
9
10
 
10
11
  # El orden de los sufijos es importante
@@ -140,16 +141,13 @@ module PgEngine
140
141
 
141
142
  def placeholder_campo(campo)
142
143
  suf = extraer_sufijo(campo)
143
- key = [controller_name, action_name, 'filter', sin_sufijo(campo)].join('.')
144
- dflt = :"activerecord.attributes.#{@clase_modelo.model_name.i18n_key}.#{sin_sufijo(campo)}"
145
- human_name = @clase_modelo.human_attribute_name(key, default: dflt)
144
+ human_name = scoped_human_attr_name(@clase_modelo, sin_sufijo(campo), 'filter')
146
145
 
147
- ret =
148
- if suf.present?
149
- "#{human_name} #{I18n.t(suf, scope: 'ransack.predicates')}"
150
- else
151
- human_name
152
- end
146
+ ret = if suf.present?
147
+ "#{human_name} #{I18n.t(suf, scope: 'ransack.predicates')}"
148
+ else
149
+ human_name
150
+ end
153
151
 
154
152
  ret.strip.downcase.tap { _1[0] = _1[0].capitalize }
155
153
  end