pg_rails 7.6.1 → 7.6.3

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +3 -0
  3. data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +22 -3
  4. data/pg_engine/app/components/bad_user_input_component.rb +1 -0
  5. data/pg_engine/app/components/inline_edit/inline_edit_component.html.slim +14 -0
  6. data/pg_engine/app/components/inline_edit/inline_edit_component.rb +23 -0
  7. data/pg_engine/app/components/inline_edit/inline_show_component.html.slim +8 -0
  8. data/pg_engine/app/components/inline_edit/inline_show_component.rb +15 -0
  9. data/pg_engine/app/controllers/concerns/pg_engine/resource.rb +8 -4
  10. data/pg_engine/app/controllers/pg_engine/base_controller.rb +7 -16
  11. data/pg_engine/app/controllers/pg_engine/health_controller.rb +30 -0
  12. data/pg_engine/app/controllers/users/inline_edit_controller.rb +21 -0
  13. data/pg_engine/app/controllers/users/registrations_controller.rb +1 -1
  14. data/pg_engine/app/decorators/pg_engine/base_record_decorator.rb +2 -2
  15. data/pg_engine/app/helpers/pg_engine/index_helper.rb +1 -1
  16. data/pg_engine/app/lib/pg_form_builder.rb +18 -0
  17. data/pg_engine/app/models/pg_engine/base_record.rb +5 -1
  18. data/pg_engine/app/views/pg_engine/base/index.html.slim +2 -1
  19. data/pg_engine/config/initializers/zeitwerk.rb +2 -0
  20. data/pg_engine/config/locales/es.yml +5 -0
  21. data/pg_engine/config/routes.rb +4 -0
  22. data/pg_engine/config/simple_form/simple_form_bootstrap.rb +17 -2
  23. data/pg_engine/lib/pg_engine.rb +2 -0
  24. data/pg_engine/spec/controllers/pg_engine/base_controller_spec.rb +1 -16
  25. data/pg_engine/spec/requests/users/accounts_spec.rb +1 -2
  26. data/pg_engine/spec/requests/users/inline_edit_spec.rb +101 -0
  27. data/pg_engine/spec/requests/users/registrations_spec.rb +19 -0
  28. data/pg_layout/app/javascript/config/index.js +20 -20
  29. data/pg_layout/app/javascript/config/turbo_rails/index.js +4 -2
  30. data/pg_rails/lib/version.rb +1 -1
  31. data/pg_rails/scss/pg_rails.scss +4 -4
  32. data/pg_scaffold/lib/generators/pg_active_record/model/templates/model.rb +3 -0
  33. data/pg_scaffold/lib/generators/pg_slim/templates/_form.html.slim +1 -1
  34. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e8749da8dcc608499fc64c9af83a9889069d9384e861508688c40e41b33ae52
4
- data.tar.gz: 51fcd1231c11477379842419333ceda58bd14b1ca5d058fdf0775b92b6caf867
3
+ metadata.gz: 7c827faeb7ffa9b904395708a6f9c6184a930ee22663bcec472b600046497baa
4
+ data.tar.gz: bb256bb482b6697302d7e3b2131f5cd9e3ddeaa17447f739b943091a6b90c431
5
5
  SHA512:
6
- metadata.gz: 06c02fbcf25d4eb7c34c8272d9b3e3f0af6a93052a882cdc8adb1f17452b39dc002145e19cd17321e6a273216393309e7b9d0efac307aa9fc33b9348fd9dc999
7
- data.tar.gz: 2a3bc5f762458f385813fbba264e9644843680d6d924a77bbb671711bd6c3fe06adb14cae59453dc38f2614fbefda264101f12106f0720e12c66c8d2bd967c39
6
+ metadata.gz: c06b67c6b8bdeb5462db606077e29b1d1a68bc58ee65a6df7c9e21a13615f723d68189a497b769faf401160875741caf946010f6b162659aa5d9c8dffec6f35d
7
+ data.tar.gz: 471922d32d56e46a8e7d20ecd0fe2793d22849a66d813c8fc103a51a8e1d44d4d8229f1b04457d0b3f16f9a9c6ab6ea052dac95dcd43605b9120ddaf8eeb8a88
@@ -41,6 +41,7 @@ module PgAssociable
41
41
  user = Current.user
42
42
  puede_crear = !template.using_modal? && Pundit::PolicyFinder.new(klass).policy.new(user, klass).new?
43
43
  collection = Pundit::PolicyFinder.new(klass).scope.new(user, klass).resolve
44
+ collection = collection.kept if collection.respond_to?(:kept)
44
45
  [collection, puede_crear]
45
46
  end
46
47
 
@@ -50,9 +51,11 @@ module PgAssociable
50
51
  options.deep_merge!({ wrapper_html: { 'data-preload': collection.decorate.to_json } })
51
52
  end
52
53
  # TODO: usar una clase más precisa para el modal?
54
+ # quizás sea este el motivo por el cual no funciona el multimodal
53
55
  options.deep_merge!({ wrapper_html: { data: { controller: 'asociable',
54
56
  'asociable-modal-outlet': '.modal' } } })
55
57
  options[:as] = 'pg_associable'
58
+
56
59
  association atributo, options
57
60
  end
58
61
 
@@ -73,9 +73,10 @@ input[type=datetime-local], input[type=datetime] {
73
73
  display: flex;
74
74
  justify-content: flex-end;
75
75
  gap: 4px;
76
- }
77
- .listado .btn-sm {
78
- padding: 0em 0.3em;
76
+
77
+ .btn-sm {
78
+ padding: 0em 0.3em;
79
+ }
79
80
  }
80
81
 
81
82
  .list-group-item {
@@ -199,3 +200,21 @@ input[type=datetime-local], input[type=datetime] {
199
200
  }
200
201
  --fc-neutral-bg-color: none;
201
202
  }
203
+
204
+ // Inline edit
205
+
206
+ .inline-edit {
207
+ // text-decoration: underline;
208
+ // text-decoration-style: dotted;
209
+ // text-decoration-color: #262626;
210
+
211
+ a:hover {
212
+ i {
213
+ color: black;
214
+ }
215
+ }
216
+
217
+ input[type=text] {
218
+ min-width: 22em;
219
+ }
220
+ }
@@ -1,3 +1,4 @@
1
+ # TODO: rename to WarningComponent or sth
1
2
  class BadUserInputComponent < BaseComponent
2
3
  def initialize(error_msg:)
3
4
  @error_msg = error_msg
@@ -0,0 +1,14 @@
1
+ = helpers.turbo_frame_tag(@frame_id, class: 'inline-edit')
2
+ = helpers.pg_form_for(@model, render_errors: false, wrapper_mappings: @wrapper_mappings,
3
+ html: { class: 'd-flex align-items-start gap-1' }) do |f|
4
+ = hidden_field_tag :inline_attribute, @attribute
5
+
6
+ = f.field @attribute, label: false, html5: true
7
+
8
+ = button_tag class: 'btn btn-sm btn-primary',
9
+ data: { controller: 'tooltip', 'bs-title': 'Guardar' } do
10
+ i.bi.bi-check-lg
11
+ = link_to users_inline_show_path(model: @model.to_gid, attribute: @attribute),
12
+ class: 'btn btn-sm btn-secondary',
13
+ data: { controller: 'tooltip', 'bs-title': 'Cancelar' } do
14
+ i.bi.bi-x-lg
@@ -0,0 +1,23 @@
1
+ class InlineEditComponent < ViewComponent::Base
2
+ def initialize(model, attribute)
3
+ @model = model
4
+ @attribute = attribute
5
+ @frame_id = dom_id(model, "#{attribute}_inline_edit")
6
+
7
+ @wrapper_mappings = {
8
+ string: :inline_form_control,
9
+ pg_associable: :inline_form_control,
10
+ date: :inline_form_control,
11
+ datetime: :inline_form_control,
12
+ select: :inline_form_select
13
+ }
14
+
15
+ super
16
+ end
17
+
18
+ def before_render
19
+ return unless controller.in_modal?
20
+
21
+ controller.instance_variable_set(:@using_modal, true)
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+ - if @model.class.inline_editable?(@attribute) && helpers.policy(@model).edit?
2
+ = helpers.turbo_frame_tag(@frame_id, class: 'inline-edit')
3
+ span = @model.decorate.send(@attribute)
4
+ = link_to users_inline_edit_path(model: @model.to_gid, attribute: @attribute),
5
+ class: 'text-body-tertiary ms-1', style: 'font-size: 0.8em' do
6
+ i.bi.bi-pencil
7
+ - else
8
+ = @model.decorate.send(@attribute)
@@ -0,0 +1,15 @@
1
+ class InlineShowComponent < ViewComponent::Base
2
+ def initialize(model, attribute)
3
+ @model = model
4
+ @attribute = attribute
5
+ @frame_id = dom_id(model, "#{attribute}_inline_edit")
6
+
7
+ super
8
+ end
9
+
10
+ def before_render
11
+ return unless controller.in_modal?
12
+
13
+ controller.instance_variable_set(:@using_modal, true)
14
+ end
15
+ end
@@ -257,7 +257,9 @@ module PgEngine
257
257
  if (@saved = object.save)
258
258
  respond_to do |format|
259
259
  format.html do
260
- if in_modal?
260
+ if params[:inline_attribute].present?
261
+ render InlineShowComponent.new(object, params[:inline_attribute]), layout: false
262
+ elsif in_modal?
261
263
  body = <<~HTML.html_safe
262
264
  <pg-event data-event-name="pg:record-updated" data-turbo-temporary
263
265
  data-response='#{object.decorate.to_json}'></pg-event>
@@ -272,6 +274,9 @@ module PgEngine
272
274
  render json: object.decorate.as_json
273
275
  end
274
276
  end
277
+ elsif params[:inline_attribute].present?
278
+ render InlineEditComponent.new(object, params[:inline_attribute]),
279
+ layout: false, status: :unprocessable_entity
275
280
  else
276
281
  add_breadcrumb instancia_modelo.decorate.to_s_short, instancia_modelo.decorate.target_object
277
282
  add_breadcrumb 'Modificando'
@@ -409,17 +414,16 @@ module PgEngine
409
414
  def set_instancia_modelo
410
415
  if action_name.in? %w[new create]
411
416
  self.instancia_modelo = clase_modelo.new(modelo_params)
417
+ authorize(instancia_modelo)
412
418
  if nested_id.present?
413
419
  instancia_modelo.send("#{nested_key}=", nested_id)
414
420
  end
415
421
  else
416
422
  self.instancia_modelo = buscar_instancia
417
-
423
+ authorize(instancia_modelo)
418
424
  instancia_modelo.assign_attributes(modelo_params) if action_name.in? %w[update]
419
425
  end
420
426
 
421
- authorize(instancia_modelo)
422
-
423
427
  # TODO: problema en create y update cuando falla la validacion
424
428
  # Reproducir el error antes de arreglarlo
425
429
  self.instancia_modelo = instancia_modelo.decorate if action_name.in? %w[show edit new]
@@ -216,22 +216,13 @@ module PgEngine
216
216
  end
217
217
 
218
218
  def not_authorized(_arg_required_for_active_admin)
219
- respond_to do |format|
220
- format.json do
221
- render json: { error: 'Acceso no autorizado' },
222
- status: :unprocessable_entity
223
- end
224
- # TODO: responder a turbo_stream
225
- format.html do
226
- if request.path == root_path
227
- # TODO!: renderear un 500.html y pg_err
228
- sign_out(Current.user) if Current.user.present?
229
- render plain: 'Acceso no autorizado', status: :unprocessable_entity
230
- else
231
- go_back('Acceso no autorizado')
232
- end
233
- end
234
- end
219
+ pg_warn <<~STRING
220
+ Acceso no autorizado.
221
+ User: #{Current.user.inspect}
222
+ Request: #{request.inspect}
223
+ STRING
224
+
225
+ render_my_component(BadUserInputComponent.new(error_msg: 'Acceso no autorizado'), :unauthorized)
235
226
  end
236
227
 
237
228
  def go_back(message = nil, type: :alert)
@@ -8,6 +8,7 @@ module PgEngine
8
8
  def show
9
9
  check_redis
10
10
  check_postgres
11
+ check_websocket
11
12
  render_up
12
13
  end
13
14
 
@@ -25,6 +26,35 @@ module PgEngine
25
26
  raise PgEngine::Error, 'redis is down'
26
27
  end
27
28
 
29
+ # rubocop:disable Metrics/MethodLength
30
+ def check_websocket
31
+ result = nil
32
+ begin
33
+ Timeout.timeout(5) do
34
+ EM.run do
35
+ url = Rails.application.config.action_cable.url
36
+ ws = Faye::WebSocket::Client.new(url)
37
+
38
+ ws.on :message do |event|
39
+ type = JSON.parse(event.data)['type']
40
+ if type == 'welcome'
41
+ result = :success
42
+ ws.close
43
+ EM.stop
44
+ end
45
+ end
46
+ end
47
+ end
48
+ rescue Timeout::Error
49
+ raise PgEngine::Error, 'websocket server is down'
50
+ end
51
+
52
+ return if result == :success
53
+
54
+ raise PgEngine::Error, 'websocket server is down'
55
+ end
56
+ # rubocop:enable Metrics/MethodLength
57
+
28
58
  def render_up
29
59
  render html: html_status(color: '#005500')
30
60
  end
@@ -0,0 +1,21 @@
1
+ module Users
2
+ class InlineEditController < PgEngine.config.users_controller
3
+ before_action do
4
+ if current_turbo_frame.blank?
5
+ render_my_component(BadRequestComponent.new, :bad_request)
6
+ end
7
+ end
8
+
9
+ def edit
10
+ model = GlobalID::Locator.locate params[:model]
11
+ attribute = params[:attribute]
12
+ render InlineEditComponent.new(model, attribute), layout: false
13
+ end
14
+
15
+ def show
16
+ model = GlobalID::Locator.locate params[:model]
17
+ attribute = params[:attribute]
18
+ render InlineShowComponent.new(model, attribute), layout: false
19
+ end
20
+ end
21
+ end
@@ -11,7 +11,7 @@ module Users
11
11
  resource.save
12
12
  yield resource if block_given?
13
13
  if resource.persisted?
14
- create_account_for(resource)
14
+ create_account_for(resource) if ActsAsTenant.current_tenant.blank?
15
15
 
16
16
  expire_data_after_sign_in!
17
17
  render_message
@@ -118,8 +118,8 @@ module PgEngine
118
118
  end
119
119
  end
120
120
 
121
- def edit_object_url
122
- helpers.url_for([:edit, target_object].flatten)
121
+ def edit_object_url(**args)
122
+ helpers.url_for([:edit, target_object, **args].flatten)
123
123
  end
124
124
 
125
125
  def new_object_url
@@ -4,7 +4,7 @@ module PgEngine
4
4
  module IndexHelper
5
5
  def column_for(object, attribute)
6
6
  content_tag :td, **column_options_for(object, attribute) do
7
- object.send(attribute).to_s
7
+ render InlineShowComponent.new(object.object, attribute)
8
8
  end
9
9
  end
10
10
 
@@ -26,6 +26,24 @@ class PgFormBuilder < SimpleForm::FormBuilder
26
26
  super
27
27
  end
28
28
 
29
+ def field(attribute_name, options = {}, &)
30
+ model = convert_to_model(object)
31
+
32
+ if find_on_all_associations(model.class, attribute_name).present?
33
+ pg_associable(attribute_name, options)
34
+ else
35
+ input(attribute_name, options, &)
36
+ end
37
+ end
38
+
39
+ def find_on_all_associations(klass, campo)
40
+ return unless klass.respond_to? :reflect_on_all_associations
41
+
42
+ klass.reflect_on_all_associations.find do |a|
43
+ a.name == campo.to_sym
44
+ end
45
+ end
46
+
29
47
  def mensajes_de_error
30
48
  # TODO: quitar en before-cache?
31
49
  title = error_notification(message: mensaje, class: 'text-danger mb-2 error-title') if mensaje
@@ -16,7 +16,11 @@ module PgEngine
16
16
  class << self
17
17
  # This is a per class variable, all subclasses of BaseRecord inherit it
18
18
  # BUT **the values are independent between all of them**
19
- attr_accessor :default_modal
19
+ attr_accessor :default_modal, :inline_editable_fields
20
+
21
+ def inline_editable?(attribute)
22
+ inline_editable_fields.present? && inline_editable_fields.include?(attribute.to_sym)
23
+ end
20
24
  end
21
25
 
22
26
  # ransacker :search do |parent|
@@ -25,7 +25,8 @@ div
25
25
  th.text-nowrap style="font-size: 0.8em" = encabezado att, ordenable: true
26
26
  th.text-end
27
27
  - unless @export_link == false
28
- = @clase_modelo.new.decorate.export_link(request.url)
28
+ .actions-wrapper
29
+ = @clase_modelo.new.decorate.export_link(request.url)
29
30
  tbody
30
31
  - @collection.each do |object|
31
32
  - object = object.decorate
@@ -6,3 +6,5 @@ Rails.autoloaders.main.ignore(
6
6
  "#{PgEngine::Engine.root}/app/overrides",
7
7
  "#{PgEngine::Engine.root}/app/views"
8
8
  )
9
+
10
+ Rails.autoloaders.main.collapse("#{PgEngine::Engine.root}/app/components/inline_edit")
@@ -16,6 +16,11 @@ es:
16
16
  index:
17
17
  empty: 'No hay %{model} que mostrar'
18
18
  empty_but_filtered: 'No hay %{model} para los filtros aplicados'
19
+ gender:
20
+ reset_password_token: m
21
+ confirmation_token: m
22
+ unlock_token: m
23
+ current_password: f
19
24
  attributes:
20
25
  external_labels: Etiquetas externas
21
26
  external_source: Fuente externa
@@ -21,6 +21,10 @@ Rails.application.routes.draw do
21
21
  registrations: 'users/registrations'
22
22
  }, failure_app: PgEngine::DeviseFailureApp
23
23
  namespace :users, path: 'u' do
24
+ scope controller: 'inline_edit', path: 'inline', as: :inline do
25
+ get 'edit'
26
+ get 'show'
27
+ end
24
28
  get 'dashboard', to: 'dashboard#dashboard'
25
29
  post 'notifications/mark_as_seen', to: 'notifications#mark_as_seen'
26
30
  post 'notifications/mark_as_unseen', to: 'notifications#mark_as_unseen'
@@ -270,7 +270,7 @@ SimpleForm.setup do |config|
270
270
  # inline forms
271
271
  #
272
272
  # inline default_wrapper
273
- config.wrappers :inline_form, class: 'col-12' do |b|
273
+ config.wrappers :inline_form_control, class: '' do |b|
274
274
  b.use :html5
275
275
  b.use :placeholder
276
276
  b.optional :maxlength
@@ -280,7 +280,22 @@ SimpleForm.setup do |config|
280
280
  b.optional :readonly
281
281
  b.use :label, class: 'visually-hidden'
282
282
 
283
- b.use :input, class: 'form-control', error_class: 'is-invalid'
283
+ b.use :input, class: 'form-control form-control-sm', error_class: 'is-invalid'
284
+ b.use :error, wrap_with: { class: 'invalid-feedback' }
285
+ b.optional :hint, wrap_with: { class: 'form-text' }
286
+ end
287
+
288
+ config.wrappers :inline_form_select, class: '' do |b|
289
+ b.use :html5
290
+ b.use :placeholder
291
+ b.optional :maxlength
292
+ b.optional :minlength
293
+ b.optional :pattern
294
+ b.optional :min_max
295
+ b.optional :readonly
296
+ b.use :label, class: 'visually-hidden'
297
+
298
+ b.use :input, class: 'form-select form-select-sm', error_class: 'is-invalid'
284
299
  b.use :error, wrap_with: { class: 'invalid-feedback' }
285
300
  b.optional :hint, wrap_with: { class: 'form-text' }
286
301
  end
@@ -55,6 +55,8 @@ require 'noticed'
55
55
  require 'ransack'
56
56
  require 'ransack_memory'
57
57
  require 'holidays'
58
+ require 'faye/websocket'
59
+ require 'eventmachine'
58
60
 
59
61
  if Rails.env.local?
60
62
  require 'letter_opener'
@@ -84,22 +84,7 @@ describe DummyBaseController do
84
84
 
85
85
  it do
86
86
  subject
87
- expect(response).to redirect_to root_path
88
- expect(flash[:alert]).to eq 'Acceso no autorizado'
89
- expect(controller).to be_user_signed_in
90
- end
91
-
92
- context 'cuando ocurre en el root_path' do
93
- before do
94
- allow_any_instance_of(ActionController::TestRequest).to receive(:path).and_return(root_path)
95
- end
96
-
97
- it do
98
- subject
99
- expect(response).to have_http_status(:unprocessable_entity)
100
- expect(response.body).to eq 'Acceso no autorizado'
101
- expect(controller).not_to be_user_signed_in
102
- end
87
+ expect(response).to have_http_status(:unauthorized)
103
88
  end
104
89
  end
105
90
 
@@ -16,7 +16,6 @@ describe 'Users::AccountsController' do
16
16
  it 'denies foreign account' do
17
17
  other_account = create :account
18
18
  get "/u/cuentas/#{other_account.to_param}"
19
- expect(response).to have_http_status(:redirect)
20
- expect(flash[:alert]).to eq 'Acceso no autorizado'
19
+ expect(response).to have_http_status(:unauthorized)
21
20
  end
22
21
  end
@@ -0,0 +1,101 @@
1
+ require 'rails_helper'
2
+
3
+ describe 'inline edit' do
4
+ before do
5
+ user = create :user
6
+ sign_in user
7
+ end
8
+
9
+ let(:cosa) { create :cosa }
10
+
11
+ describe '#show' do
12
+ subject do
13
+ get '/u/inline/show', params:, headers: { 'Turbo-Frame': turbo_frame_id }
14
+ end
15
+
16
+ let(:params) do
17
+ { model: cosa.to_gid.to_param, attribute: :nombre }
18
+ end
19
+
20
+ let(:turbo_frame_id) { 'turbo-frame' }
21
+
22
+ it do
23
+ subject
24
+ expect(response).to have_http_status(:ok)
25
+ expect(response.body).to include cosa.nombre
26
+ end
27
+
28
+ context 'when no turbo frame targetted' do
29
+ let(:turbo_frame_id) { nil }
30
+
31
+ it do
32
+ subject
33
+ expect(response).to have_http_status(:bad_request)
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#edit' do
39
+ subject do
40
+ get '/u/inline/edit', params:, headers: { 'Turbo-Frame': turbo_frame_id }
41
+ end
42
+
43
+ let(:params) do
44
+ { model: cosa.to_gid.to_param, attribute: :nombre }
45
+ end
46
+
47
+ let(:turbo_frame_id) { 'turbo-frame' }
48
+
49
+ it do
50
+ subject
51
+ expect(response).to have_http_status(:ok)
52
+ expect(response.body).to include cosa.nombre
53
+ end
54
+
55
+ context 'when no turbo frame targetted' do
56
+ let(:turbo_frame_id) { nil }
57
+
58
+ it do
59
+ subject
60
+ expect(response).to have_http_status(:bad_request)
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '#update' do
66
+ subject do
67
+ patch "/u/cosas/#{cosa.to_param}", params:
68
+ end
69
+
70
+ let(:params) do
71
+ {
72
+ inline_attribute: 'nombre',
73
+ cosa: {
74
+ nombre:
75
+ }
76
+ }
77
+ end
78
+
79
+ let(:nombre) { 'otro' }
80
+
81
+ it do
82
+ subject
83
+ expect(response.body).to include 'turbo-frame'
84
+ expect(response).to have_http_status(:ok)
85
+ end
86
+
87
+ it do
88
+ expect { subject }.to change { cosa.reload.nombre }.to 'otro'
89
+ end
90
+
91
+ context 'when validation fails' do
92
+ let(:nombre) { nil }
93
+
94
+ it do
95
+ subject
96
+ expect(response.body).to include 'turbo-frame'
97
+ expect(response).to have_http_status(:unprocessable_entity)
98
+ end
99
+ end
100
+ end
101
+ end
@@ -69,6 +69,25 @@ describe 'registrations controller' do
69
69
  expect { subject }.not_to change(Account, :count)
70
70
  end
71
71
  end
72
+
73
+ context 'cuando hay tenant' do
74
+ before do
75
+ host! 'bien.localhost.com'
76
+ create :account, subdomain: 'bien'
77
+ end
78
+
79
+ it do
80
+ expect { subject }.to change(User, :count).by(1)
81
+ end
82
+
83
+ it do
84
+ expect { subject }.to change(UserAccount, :count).by(1)
85
+ end
86
+
87
+ it do
88
+ expect { subject }.not_to change(Account, :count)
89
+ end
90
+ end
72
91
  end
73
92
 
74
93
  describe '#edit' do
@@ -7,23 +7,23 @@ import './tooltips'
7
7
  import 'trix'
8
8
  import '@rails/actiontext'
9
9
 
10
- function bindListingClick () {
11
- document.body.onclick = (ev) => {
12
- if (ev.target.closest('a')) return
13
- if (ev.target.closest('.listado')) {
14
- const row = ev.target.closest('tr')
15
- if (row) {
16
- const show = row.querySelector('.bi-eye-fill')
17
- if (show) {
18
- const link = show.closest('a')
19
- if (link) {
20
- link.click()
21
- }
22
- }
23
- }
24
- }
25
- }
26
- }
27
- bindListingClick()
28
- document.addEventListener('turbo:load', bindListingClick)
29
- document.addEventListener('turbo:render', bindListingClick)
10
+ // function bindListingClick () {
11
+ // document.body.onclick = (ev) => {
12
+ // if (ev.target.closest('a')) return
13
+ // if (ev.target.closest('.listado')) {
14
+ // const row = ev.target.closest('tr')
15
+ // if (row) {
16
+ // const show = row.querySelector('.bi-eye-fill')
17
+ // if (show) {
18
+ // const link = show.closest('a')
19
+ // if (link) {
20
+ // link.click()
21
+ // }
22
+ // }
23
+ // }
24
+ // }
25
+ // }
26
+ // }
27
+ // bindListingClick()
28
+ // document.addEventListener('turbo:load', bindListingClick)
29
+ // document.addEventListener('turbo:render', bindListingClick)
@@ -23,8 +23,10 @@ document.addEventListener('turbo:before-cache', () => {
23
23
  })
24
24
  })
25
25
 
26
- document.addEventListener('turbo:frame-missing', (ev) => {
27
- Rollbar.error('Turbo Frame missing')
26
+ document.addEventListener('turbo:frame-missing', async (ev) => {
27
+ const text = await ev.detail.response.text()
28
+ Rollbar.error('turbo:frame-missing', text, ev.detail)
29
+ console.error('turbo:frame-missing', text, ev.detail)
28
30
  ev.preventDefault()
29
31
  const html = `
30
32
  <div>
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRails
4
- VERSION = '7.6.1'
4
+ VERSION = '7.6.3'
5
5
  end
@@ -89,7 +89,7 @@ $light-border-emphasis: shade-color($light, 60%);
89
89
  border: 1px solid $light-border-emphasis;
90
90
  }
91
91
 
92
- .listado tr:has(td:hover):has(.bi-eye-fill) td {
93
- background-color: #f2f2f2;
94
- cursor: pointer;
95
- }
92
+ // .listado tr:has(td:hover):has(.bi-eye-fill) td {
93
+ // background-color: #f2f2f2;
94
+ // cursor: pointer;
95
+ // }
@@ -15,6 +15,9 @@ class <%= class_name %> < <%= parent_class_name.classify %>
15
15
  <%- if options[:discard] -%>
16
16
  include Discard::Model
17
17
  <%- end -%>
18
+
19
+ self.default_modal = true
20
+ self.inline_editable_fields = %i[<%= attributes.map(&:name).join(' ') %>]
18
21
  <%- if attributes.any?(&:reference?) -%>
19
22
 
20
23
  <%- attributes.select(&:reference?).each do |attribute| -%>
@@ -3,7 +3,7 @@
3
3
  div style="max-width: 22em" data-controller="pg_form"
4
4
  = pg_form_for(@<%= singular_name %> || object) do |f|
5
5
  <%- attributes.each do |attribute| -%>
6
- = f.<%= attribute.reference? ? :pg_associable : :input %> :<%= attribute.name %>
6
+ = f.field :<%= attribute.name %>
7
7
  <%- end -%>
8
8
  .mt-2
9
9
  = f.button :submit
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.6.1
4
+ version: 7.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martín Rosso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-30 00:00:00.000000000 Z
11
+ date: 2024-10-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '5.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: faye-websocket
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.11'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.11'
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: acts_as_tenant
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -660,6 +674,10 @@ files:
660
674
  - pg_engine/app/components/date_selector_component.html.slim
661
675
  - pg_engine/app/components/date_selector_component.rb
662
676
  - pg_engine/app/components/flash_container_component.rb
677
+ - pg_engine/app/components/inline_edit/inline_edit_component.html.slim
678
+ - pg_engine/app/components/inline_edit/inline_edit_component.rb
679
+ - pg_engine/app/components/inline_edit/inline_show_component.html.slim
680
+ - pg_engine/app/components/inline_edit/inline_show_component.rb
663
681
  - pg_engine/app/components/internal_error_component.rb
664
682
  - pg_engine/app/components/modal_component.html.slim
665
683
  - pg_engine/app/components/modal_component.rb
@@ -693,6 +711,7 @@ files:
693
711
  - pg_engine/app/controllers/users/confirmations_controller.rb
694
712
  - pg_engine/app/controllers/users/dashboard_controller.rb
695
713
  - pg_engine/app/controllers/users/date_jumper_controller.rb
714
+ - pg_engine/app/controllers/users/inline_edit_controller.rb
696
715
  - pg_engine/app/controllers/users/notifications_controller.rb
697
716
  - pg_engine/app/controllers/users/registrations_controller.rb
698
717
  - pg_engine/app/decorators/account_decorator.rb
@@ -888,6 +907,7 @@ files:
888
907
  - pg_engine/spec/requests/users/base_controller_spec.rb
889
908
  - pg_engine/spec/requests/users/dashboard_spec.rb
890
909
  - pg_engine/spec/requests/users/date_jumper_spec.rb
910
+ - pg_engine/spec/requests/users/inline_edit_spec.rb
891
911
  - pg_engine/spec/requests/users/registrations_spec.rb
892
912
  - pg_engine/spec/requests/users/switcher_spec.rb
893
913
  - pg_engine/spec/system/alerts_spec.rb