pg_rails 7.6.17 → 7.6.19

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