pg_rails 7.6.1 → 7.6.3

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