pg_rails 7.0.1 → 7.0.2

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 (162) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -4
  3. data/pg_associable/app/assets/css/pg_associable.scss +99 -0
  4. data/pg_associable/app/assets/js/asociable_controller.js +59 -0
  5. data/pg_associable/app/assets/js/asociable_inline_controller.js +141 -0
  6. data/pg_associable/app/assets/js/modal_controller.js +130 -0
  7. data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +31 -0
  8. data/pg_associable/app/helpers/pg_associable/helpers.rb +19 -0
  9. data/pg_associable/app/inputs/pg_associable/pg_associable_inline_input.rb +39 -0
  10. data/pg_associable/app/inputs/pg_associable/pg_associable_input.rb +41 -0
  11. data/pg_associable/app/views/pg_associable/_resultados.html.slim +9 -0
  12. data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +12 -0
  13. data/pg_associable/app/views/pg_engine/base/_pg_associable_modal.html.slim +39 -0
  14. data/pg_associable/index.js +7 -0
  15. data/pg_associable/lib/pg_associable/engine.rb +12 -0
  16. data/pg_associable/lib/pg_associable/simple_form_initializer.rb +34 -0
  17. data/pg_associable/lib/pg_associable.rb +5 -0
  18. data/{lib/tasks/pg_rails_tasks.rake → pg_associable/lib/tasks/pg_associable_tasks.rake} +1 -2
  19. data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +74 -0
  20. data/pg_engine/app/controllers/pg_engine/base_controller.rb +365 -0
  21. data/pg_engine/app/controllers/pg_engine/devise_controller.rb +9 -0
  22. data/pg_engine/app/controllers/pg_engine/signed_in_controller.rb +7 -0
  23. data/{app/decorators/pg_rails → pg_engine/app/decorators/pg_engine}/base_decorator.rb +21 -23
  24. data/pg_engine/app/helpers/pg_engine/flash_helper.rb +26 -0
  25. data/pg_engine/app/helpers/pg_engine/form_helper.rb +33 -0
  26. data/pg_engine/app/helpers/pg_engine/index_helper.rb +42 -0
  27. data/{app/helpers/pg_rails → pg_engine/app/helpers/pg_engine}/postgres_helper.rb +1 -1
  28. data/{app/helpers/pg_rails → pg_engine/app/helpers/pg_engine}/print_helper.rb +15 -31
  29. data/pg_engine/app/helpers/pg_engine/route_helper.rb +41 -0
  30. data/pg_engine/app/inputs/pg_engine/fecha_input.rb +20 -0
  31. data/{app/lib/pg_rails → pg_engine/app/lib/pg_engine}/filtros_builder.rb +16 -17
  32. data/pg_engine/app/lib/pg_form_builder.rb +22 -0
  33. data/pg_engine/app/models/pg_engine/base_record.rb +63 -0
  34. data/{app/policies/pg_rails → pg_engine/app/policies/pg_engine}/application_policy.rb +2 -2
  35. data/pg_engine/app/views/pg_engine/base/download.xlsx.axlsx +14 -0
  36. data/pg_engine/app/views/pg_engine/base/index.html.slim +51 -0
  37. data/pg_engine/config/locales/es.yml +48 -0
  38. data/pg_engine/config/simple_form/simple_form.rb +178 -0
  39. data/pg_engine/config/simple_form/simple_form_bootstrap.rb +371 -0
  40. data/{lib/pg_rails → pg_engine/lib/pg_engine}/configuracion.rb +3 -1
  41. data/pg_engine/lib/pg_engine/engine.rb +53 -0
  42. data/{lib/pg_rails → pg_engine/lib/pg_engine}/utils/logueador.rb +8 -1
  43. data/pg_engine/lib/pg_engine.rb +35 -0
  44. data/{lib → pg_engine/lib}/tasks/auto_anotar_modelos.rake +1 -1
  45. data/pg_engine/lib/templates/activeadmin/audits.rb +53 -0
  46. data/pg_engine/lib/templates/activeadmin/users.rb +54 -0
  47. data/pg_layout/app/assets/stylesheets/sidebar.scss +106 -0
  48. data/pg_layout/app/javascript/cookies.js +23 -0
  49. data/pg_layout/app/javascript/navbar_controller.js +10 -0
  50. data/pg_layout/app/lib/navbar.rb +61 -0
  51. data/pg_layout/app/views/devise/confirmations/new.html.erb +20 -0
  52. data/pg_layout/app/views/devise/mailer/confirmation_instructions.html.erb +5 -0
  53. data/pg_layout/app/views/devise/mailer/email_changed.html.erb +7 -0
  54. data/pg_layout/app/views/devise/mailer/password_change.html.erb +3 -0
  55. data/pg_layout/app/views/devise/mailer/reset_password_instructions.html.erb +8 -0
  56. data/pg_layout/app/views/devise/mailer/unlock_instructions.html.erb +7 -0
  57. data/pg_layout/app/views/devise/passwords/edit.html.erb +27 -0
  58. data/pg_layout/app/views/devise/passwords/new.html.erb +18 -0
  59. data/pg_layout/app/views/devise/registrations/edit.html.erb +35 -0
  60. data/pg_layout/app/views/devise/registrations/new.html.erb +25 -0
  61. data/pg_layout/app/views/devise/sessions/new.html.erb +20 -0
  62. data/pg_layout/app/views/devise/shared/_error_messages.html.erb +15 -0
  63. data/pg_layout/app/views/devise/shared/_links.html.erb +25 -0
  64. data/pg_layout/app/views/devise/unlocks/new.html.erb +19 -0
  65. data/pg_layout/app/views/kaminari/_first_page.html.slim +3 -0
  66. data/pg_layout/app/views/kaminari/_gap.html.slim +2 -0
  67. data/pg_layout/app/views/kaminari/_last_page.html.slim +3 -0
  68. data/pg_layout/app/views/kaminari/_next_page.html.slim +3 -0
  69. data/pg_layout/app/views/kaminari/_page.html.slim +6 -0
  70. data/pg_layout/app/views/kaminari/_paginator.html.slim +12 -0
  71. data/pg_layout/app/views/kaminari/_prev_page.html.slim +3 -0
  72. data/pg_layout/app/views/layouts/pg_layout/devise.html.slim +24 -0
  73. data/pg_layout/app/views/layouts/pg_layout/layout.html.slim +30 -0
  74. data/pg_layout/app/views/pg_layout/_flash.html.slim +10 -0
  75. data/pg_layout/app/views/pg_layout/_navbar.html.erb +43 -0
  76. data/pg_layout/app/views/pg_layout/_sidebar.html.erb +42 -0
  77. data/pg_layout/index.js +35 -0
  78. data/pg_layout/lib/pg_layout/engine.rb +7 -0
  79. data/pg_layout/lib/pg_layout.rb +9 -0
  80. data/pg_rails/js/index.js +2 -0
  81. data/pg_rails/lib/pg_rails.rb +7 -0
  82. data/{lib/pg_rails → pg_rails/lib}/version.rb +1 -1
  83. data/pg_rails/scss/pg_rails.scss +3 -0
  84. data/pg_scaffold/lib/generators/pg_active_record/model/model_generator.rb +34 -0
  85. data/pg_scaffold/lib/generators/pg_active_record/model/templates/admin.rb +19 -0
  86. data/pg_scaffold/lib/generators/pg_active_record/model/templates/create_table_migration.rb.tt +46 -0
  87. data/pg_scaffold/lib/generators/pg_active_record/model/templates/migration.rb.tt +48 -0
  88. data/pg_scaffold/lib/generators/pg_active_record/model/templates/model.rb +47 -0
  89. data/pg_scaffold/lib/generators/pg_active_record/model/templates/module.rb +9 -0
  90. data/pg_scaffold/lib/generators/pg_decorator/USAGE +8 -0
  91. data/pg_scaffold/lib/generators/pg_decorator/pg_decorator_generator.rb +31 -0
  92. data/pg_scaffold/lib/generators/pg_decorator/templates/decorator.rb +22 -0
  93. data/pg_scaffold/lib/generators/pg_factory_bot/model/model_generator.rb +95 -0
  94. data/pg_scaffold/lib/generators/pg_factory_bot/model/templates/factories.erb +14 -0
  95. data/pg_scaffold/lib/generators/pg_pundit/USAGE +8 -0
  96. data/pg_scaffold/lib/generators/pg_pundit/pg_pundit_generator.rb +21 -0
  97. data/pg_scaffold/lib/generators/pg_pundit/templates/policy.rb +37 -0
  98. data/pg_scaffold/lib/generators/pg_rails/instalar/USAGE +8 -0
  99. data/pg_scaffold/lib/generators/pg_rails/instalar/instalar_generator.rb +17 -0
  100. data/pg_scaffold/lib/generators/pg_rails/instalar/templates/pg_rails.rb +10 -0
  101. data/pg_scaffold/lib/generators/pg_resource_route/pg_resource_route_generator.rb +27 -0
  102. data/pg_scaffold/lib/generators/pg_rspec/model/model_generator.rb +30 -0
  103. data/pg_scaffold/lib/generators/pg_rspec/model/templates/model_spec.rb +15 -0
  104. data/pg_scaffold/lib/generators/pg_rspec/scaffold/scaffold_generator.rb +43 -0
  105. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/api_controller_spec.rb +167 -0
  106. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/controller_spec.rb +269 -0
  107. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/edit_spec.rb +34 -0
  108. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/index_spec.rb +28 -0
  109. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/new_spec.rb +34 -0
  110. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/routing_spec.rb +50 -0
  111. data/pg_scaffold/lib/generators/pg_rspec/scaffold/templates/show_spec.rb +26 -0
  112. data/pg_scaffold/lib/generators/pg_scaffold/USAGE +8 -0
  113. data/pg_scaffold/lib/generators/pg_scaffold/pg_scaffold_generator.rb +87 -0
  114. data/pg_scaffold/lib/generators/pg_scaffold/templates/controller.rb +37 -0
  115. data/pg_scaffold/lib/generators/pg_slim/USAGE +8 -0
  116. data/pg_scaffold/lib/generators/pg_slim/pg_slim_generator.rb +31 -0
  117. data/pg_scaffold/lib/generators/pg_slim/templates/_form.html.slim +12 -0
  118. data/pg_scaffold/lib/generators/pg_slim/templates/download.xlsx.axlsx +14 -0
  119. data/pg_scaffold/lib/generators/pg_slim/templates/edit.html.slim +5 -0
  120. data/pg_scaffold/lib/generators/pg_slim/templates/index.html.slim +51 -0
  121. data/pg_scaffold/lib/generators/pg_slim/templates/new.html.slim +5 -0
  122. data/pg_scaffold/lib/generators/pg_slim/templates/partial.html.slim +1 -0
  123. data/pg_scaffold/lib/generators/pg_slim/templates/show.html.slim +38 -0
  124. data/pg_scaffold/lib/pg_scaffold/monkey_patches/mejoras_a_named_base.rb +37 -0
  125. data/pg_scaffold/lib/pg_scaffold/monkey_patches/mejoras_de_atributos.rb +116 -0
  126. data/pg_scaffold/lib/pg_scaffold/railtie.rb +16 -0
  127. data/pg_scaffold/lib/pg_scaffold.rb +4 -0
  128. metadata +134 -48
  129. data/Rakefile +0 -36
  130. data/app/assets/javascripts/pg_rails/asociacion_creable.js +0 -85
  131. data/app/assets/javascripts/pg_rails/best_in_place_datepicker.js +0 -58
  132. data/app/assets/javascripts/pg_rails/librerias.js +0 -13
  133. data/app/assets/javascripts/pg_rails/librerias_b3.js +0 -14
  134. data/app/assets/javascripts/pg_rails/validaciones.js +0 -44
  135. data/app/assets/javascripts/pg_rails.js +0 -318
  136. data/app/assets/stylesheets/pg_rails/librerias.scss +0 -5
  137. data/app/assets/stylesheets/pg_rails/pg_chosen.scss +0 -29
  138. data/app/assets/stylesheets/pg_rails/pg_rails.scss +0 -199
  139. data/app/assets/stylesheets/pg_rails_b3.scss +0 -10
  140. data/app/assets/stylesheets/pg_rails_b4.scss +0 -12
  141. data/app/assets/stylesheets/pg_rails_b5.scss +0 -1
  142. data/app/controllers/pg_rails/application_controller.rb +0 -316
  143. data/app/controllers/pg_rails/editar_en_lugar_controller.rb +0 -24
  144. data/app/helpers/pg_rails/editar_en_lugar_helper.rb +0 -106
  145. data/app/helpers/pg_rails/form_helper.rb +0 -25
  146. data/app/inputs/pg_rails/asociacion_creable_input.rb +0 -72
  147. data/app/inputs/pg_rails/fecha_input.rb +0 -20
  148. data/app/inputs/pg_rails/selects_dependientes_input.rb +0 -9
  149. data/app/lib/pg_form_builder.rb +0 -31
  150. data/app/models/pg_rails/application_record.rb +0 -51
  151. data/app/views/application/_abrir_modal.js.erb +0 -14
  152. data/app/views/application/_actualizar_smart_listing.html.slim +0 -3
  153. data/app/views/application/_cerrar_modal.js.erb +0 -8
  154. data/app/views/application/_modal_ajax_form.js.erb +0 -7
  155. data/config/brakeman.ignore +0 -42
  156. data/config/locales/es.yml +0 -17
  157. data/config/routes.rb +0 -3
  158. data/config/spring.rb +0 -1
  159. data/lib/pg_rails/engine.rb +0 -42
  160. data/lib/pg_rails/simple_form/initializer.rb +0 -583
  161. data/lib/pg_rails.rb +0 -23
  162. /data/{lib/pg_rails → pg_engine/lib/pg_engine}/core_ext.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c90f078b433e7dd978b63f547224e2a1d101d106ac6a8427b69878f3a2a206d
4
- data.tar.gz: f215ee9ca33ed7598e2251877b2ee3b0c3694349aded4515b0a24e2a0362b100
3
+ metadata.gz: 6fbc8593bcefeb2a4c90325240059ad131837f569c9536415c2de05cc43c87d4
4
+ data.tar.gz: c99f069a6d7339c335ad74f8e635c9b5878b159045c04d2875a7a5caa340fea1
5
5
  SHA512:
6
- metadata.gz: 5e0cd23bbf7ce79fb2e5dcaa73b7c0d9c24ca87811b46daf20c73dc72315a4d707145f178a976bcb89ea23a55431ff31d951a140ab5838771a0b3d35f95169c1
7
- data.tar.gz: 85f3141ae4506303936a93b078e8d0f6a9254c2b50784b9f158672db90e6f446e2087624229f1e4ef682beb4c4ccd41d94785454d991ec2b5c4572997395826d
6
+ metadata.gz: 980e5e6f831e0c24c0c07c1e16805dbe042a40ad47924f42a2827d3532cd469c4e58091fc2036e771ddb8ab9387b1a59ce370da503de7a3c8a202a18d8073157
7
+ data.tar.gz: 514b5334497d070b1a1b9933920984048bf4c58a741af3be050f09c971e4c365bdf85a02227049cacca7b50e5128017553028b3bb6fc358e26a5ad38a28d4629
data/README.md CHANGED
@@ -65,9 +65,11 @@ Acceder a la app en <http://localhost:3000/>.
65
65
 
66
66
  ### Regenerar modelos dummy
67
67
 
68
- 1. `bundle exec rails destroy Cosa`
69
- 2. `bundle exec rails destroy CategoriaDeCosa`
68
+ cd spec/dummy
69
+
70
+ 1. `bundle exec rails d pg_scaffold Admin/Cosa`
71
+ 2. `bundle exec rails d pg_scaffold Admin/CategoriaDeCosa`
70
72
  3. Borrar policies
71
- 4. `rails g pg_scaffold CategoriaDeCosa nombre:string{required} "tipo:integer{enum,required}" fecha:date tiempo:datetime --discard`
72
- 5. `rails g pg_scaffold Cosa nombre:string{required} "tipo:integer{enum,required}" categoria_de_cosa:references{required} --discard`
73
+ 4. `bundle exec rails g pg_scaffold Admin/CategoriaDeCosa nombre:string{required} "tipo:integer{enum,required}" fecha:date tiempo:datetime --model-name=CategoriaDeCosa --discard --activeadmin`
74
+ 5. `bundle exec rails g pg_scaffold Admin/Cosa nombre:string{required} "tipo:integer{enum,required}" categoria_de_cosa:references{required} --model-name=Cosa --discard --activeadmin`
73
75
  6. Setup asociacion creable en cosas/_form
@@ -0,0 +1,99 @@
1
+ @use 'sass:color';
2
+
3
+ [data-state=select-item] .show-on-new-item {
4
+ display: none;
5
+ }
6
+ [data-state=new-item] .show-on-select-item {
7
+ display: none;
8
+ }
9
+ .pg-form {
10
+ .pg_associable_inline, .pg_associable {
11
+ .limpiar {
12
+ display: none;
13
+ position: absolute;
14
+ top: 8px;
15
+ right: 10px;
16
+ i {
17
+ color: #6f7071;
18
+ }
19
+ }
20
+ .pencil {
21
+ position: absolute;
22
+ top: 6px;
23
+ right: 9px;
24
+ color: #6f7071;
25
+ cursor: pointer;
26
+ }
27
+ &.focus .pencil {
28
+ display: none;
29
+ }
30
+ input[type=text] {
31
+ background-color: white;
32
+ }
33
+ &.filled {
34
+ .pencil {
35
+ display: none;
36
+ }
37
+ .limpiar {
38
+ display: inline-block;
39
+ }
40
+ input, input:focus {
41
+ background-color: color.adjust(#a7b7bb, $saturation: +10%, $lightness: +20%);
42
+ }
43
+ }
44
+ .resultados-wrapper {
45
+ display: none;
46
+ position: relative;
47
+ }
48
+ &:focus-within {
49
+ .search-box {
50
+ // outline: 1px solid color.adjust(#a7b7bb, $saturation: +30%);
51
+ // border-radius: 3px;
52
+ }
53
+
54
+ &:has(.resultados) {
55
+ input[type=text] {
56
+ border-bottom-left-radius: 0!important;
57
+ border-bottom-right-radius: 0!important;
58
+ &:focus, &:focus-visible {
59
+ outline: none!important;
60
+ }
61
+ }
62
+ }
63
+ .resultados-wrapper {
64
+ display: block;
65
+ }
66
+ }
67
+ .resultados {
68
+ position: absolute;
69
+ // top: 30px;
70
+ background-color: white;
71
+ border: 1px solid #a7b7bb;
72
+ border-top: none;
73
+ border-radius: 4px;
74
+ padding: 5px 11px;
75
+ width: 100%;
76
+
77
+ border-top-left-radius: 0;
78
+ border-top-right-radius: 0;
79
+ }
80
+ }
81
+ }
82
+ .modal-asociable {
83
+ .buscar input[type=text] {
84
+ max-width: 180px;
85
+ }
86
+ .resultados {
87
+ margin-top: 16px;
88
+ border: 1px solid #c8c8c8;
89
+ }
90
+ }
91
+ .resultados {
92
+ .list-group-item:hover {
93
+ background-color: color.adjust(#a7b7bb, $saturation: +10%, $lightness: +20%);
94
+ }
95
+ }
96
+
97
+ .modal-asociable .modal-footer {
98
+ justify-content: space-between;
99
+ }
@@ -0,0 +1,59 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static outlets = ['modal']
5
+
6
+ lastValue = null
7
+
8
+ connect (e) {
9
+ console.log('connect asociable')
10
+
11
+ // ID único para identificar el campo y el modal
12
+ const elemId = Math.trunc(Math.random() * 1000000000)
13
+ this.element.setAttribute('data-asociable-modal-outlet', `.modal-${elemId}`)
14
+ this.element.classList.add(`asociable-${elemId}`)
15
+
16
+ const that = this
17
+ const modalLink = this.targets.element.querySelector('.modal-link')
18
+ const path = modalLink.attributes.href.value
19
+ modalLink.setAttribute('href', `${path}?id=${elemId}`)
20
+ modalLink.onclick = (e) => {
21
+ // Si ya hay un modal abierto lo abro y evito que se haga el get
22
+ // Si no, dejo que se ejecute el comportamiento por default
23
+ if (that.modalOutlets.length > 0) {
24
+ e.preventDefault()
25
+ that.modalOutlet.openModal()
26
+ }
27
+ }
28
+ const input = this.element.querySelector('input[type=text]')
29
+ if(input.value) {
30
+ this.element.classList.add('filled')
31
+ }
32
+
33
+ }
34
+
35
+
36
+ selectItem (e) {
37
+ // TODO: text en data
38
+ this.completarCampo(e.target.dataset.id, e.target.text)
39
+ }
40
+ completarCampo (id, text) {
41
+ const textField = this.element.querySelector('input[type=text]')
42
+ const hiddenField = this.element.querySelector('input[type=hidden]')
43
+ if( id === undefined ) {
44
+ id = null
45
+ }
46
+ hiddenField.value = id
47
+ if (id) {
48
+ textField.value = text
49
+ this.element.classList.add('filled')
50
+ } else {
51
+ textField.value = null
52
+ this.element.classList.remove('filled')
53
+ }
54
+ }
55
+
56
+ disconnect (e) {
57
+ console.log('disconnect asociable')
58
+ }
59
+ }
@@ -0,0 +1,141 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static outlets = ['modal']
5
+
6
+ result = null
7
+ elemId = null
8
+ input = null
9
+ lastValue = ''
10
+ connect (e) {
11
+ console.log('connect asociable_inline')
12
+ const that = this
13
+ // ID único para identificar el campo y el modal
14
+ this.input = this.element.querySelector('input[type=text]')
15
+ this.elemId = Math.trunc(Math.random() * 1000000000)
16
+ this.element.classList.add(`asociable-${this.elemId}`)
17
+ this.result = document.createElement('div')
18
+ this.result.setAttribute('id', `resultados-${this.elemId}`)
19
+ this.result.classList.add('resultados-wrapper')
20
+ this.input.parentNode.appendChild(this.result)
21
+ this.input.parentNode.appendChild(this.result)
22
+ this.element.querySelector('.pencil').onclick = (e) => {
23
+ that.input.focus()
24
+ }
25
+ if(this.input.value) {
26
+ this.element.classList.add('filled')
27
+ }
28
+
29
+ let debounce = function(callback, wait) {
30
+ let timerId;
31
+ return (...args) => {
32
+ clearTimeout(timerId);
33
+ timerId = setTimeout(() => {
34
+ callback(...args);
35
+ }, wait);
36
+ };
37
+ }
38
+ const doSearchBounce = debounce((force) => {
39
+ that.doSearch(force)
40
+ }, 200)
41
+
42
+ this.input.addEventListener('blur', (e) => {
43
+ this.input.placeholder = ""
44
+ })
45
+ this.input.onfocus = (e) => {
46
+ this.input.select()
47
+ if(this.input.value.length == 0) {
48
+ this.escribiAlgo()
49
+ }
50
+ }
51
+ this.input.onkeyup = (e) => {
52
+ if(this.input.value.length == 0) {
53
+ this.escribiAlgo()
54
+ }
55
+ if(e.keyCode == 13) {
56
+ doSearchBounce(true)
57
+ } else {
58
+ doSearchBounce()
59
+ }
60
+ }
61
+ this.input.onkeydown = (e) => {
62
+ if(e.keyCode == 13) {
63
+ e.preventDefault();
64
+ return false;
65
+ }
66
+ }
67
+ }
68
+ buscando() {
69
+ this.result.innerHTML = `
70
+ <div class="resultados" tabindex="-1">
71
+ <div class="fst-italic text-secondary">Buscando...</div>
72
+ </div>
73
+ `
74
+ }
75
+ escribiAlgo() {
76
+ this.input.placeholder = "Escribí algo para buscar"
77
+ }
78
+
79
+ doSearch(force = false) {
80
+ if(!force && this.input.value.length < 3) {
81
+ return
82
+ }
83
+ if(!force && this.input.value == this.lastValue) {
84
+ return
85
+ }
86
+ this.lastValue = this.input.value
87
+
88
+ let timerId = setTimeout(() => {
89
+ this.buscando()
90
+ }, 200)
91
+ document.addEventListener("turbo:before-stream-render", function(e) {
92
+ clearTimeout(timerId)
93
+ })
94
+ let url = `${this.input.dataset.url}?id=${this.elemId}`
95
+ const form = document.createElement('form')
96
+ form.setAttribute('method', 'post')
97
+ form.setAttribute('action', url)
98
+ form.setAttribute('data-turbo-stream', true)
99
+ let partial = document.createElement('input')
100
+ partial.setAttribute('type', 'hidden')
101
+ partial.setAttribute('name', 'partial')
102
+ partial.setAttribute('value', 'pg_associable/resultados_inline')
103
+ form.appendChild(partial)
104
+ let query = document.createElement('input')
105
+ query.setAttribute('type', 'hidden')
106
+ query.setAttribute('name', 'query')
107
+ query.setAttribute('value', this.input.value)
108
+ form.appendChild(query)
109
+ document.body.prepend(form)
110
+ form.requestSubmit()
111
+ form.remove()
112
+ }
113
+
114
+ completarCampo (id, text) {
115
+ const textField = this.element.querySelector('input[type=text]')
116
+ const hiddenField = this.element.querySelector('input[type=hidden]')
117
+ if( id === undefined ) {
118
+ id = null
119
+ }
120
+ hiddenField.value = id
121
+ if (id) {
122
+ textField.value = text
123
+ this.element.classList.add('filled')
124
+ } else {
125
+ this.element.classList.remove('filled')
126
+ textField.value = null
127
+ }
128
+ }
129
+
130
+ selectItem (e) {
131
+ console.log('select')
132
+ // TODO: text en data
133
+ this.completarCampo(e.target.dataset.id, e.target.text)
134
+ this.result.innerHTML = ''
135
+ }
136
+
137
+
138
+ disconnect (e) {
139
+ console.log('disconnect asociable_inline')
140
+ }
141
+ }
@@ -0,0 +1,130 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import * as bootstrap from 'bootstrap'
3
+
4
+ export default class extends Controller {
5
+ static outlets = ['asociable']
6
+ static targets = ['response', 'result']
7
+
8
+ modalPuntero = null
9
+ input = null
10
+ lastValue = ''
11
+
12
+ connect (e) {
13
+ console.log('ModalController connected')
14
+ const modal = this.targets.element
15
+ this.modalPuntero = new bootstrap.Modal(modal)
16
+ this.modalPuntero.show()
17
+
18
+ this.input = this.element.querySelector('input[type=text]')
19
+
20
+ let debounce = function(callback, wait) {
21
+ let timerId;
22
+ return (...args) => {
23
+ clearTimeout(timerId);
24
+ timerId = setTimeout(() => {
25
+ callback(...args);
26
+ }, wait);
27
+ };
28
+ }
29
+ const doSearchBounce = debounce((force) => {
30
+ this.doSearch(force)
31
+ }, 200)
32
+ this.input.onkeydown = (e) => {
33
+ if(e.keyCode == 13) {
34
+ e.preventDefault();
35
+ return false;
36
+ }
37
+ }
38
+ this.input.onkeyup = (e) => {
39
+ if(e.keyCode == 13) {
40
+ doSearchBounce(true)
41
+ } else {
42
+ doSearchBounce()
43
+ }
44
+ }
45
+ }
46
+
47
+ buscando() {
48
+ this.resultTarget.innerHTML = `
49
+ <ul class="resultados list-group list-group-flush" tabindex="-1">
50
+ <li class="list-group-item">
51
+ <span class="fst-italic text-secondary">Buscando...</span>
52
+ </li>
53
+ </ul>
54
+ `
55
+ }
56
+ doSearch(force = false) {
57
+ if(!force && this.input.value.length < 3) {
58
+ return
59
+ }
60
+ if(!force && this.input.value == this.lastValue) {
61
+ return
62
+ }
63
+ this.lastValue = this.input.value
64
+
65
+ let timerId = setTimeout(() => {
66
+ this.buscando()
67
+ }, 200)
68
+ document.addEventListener("turbo:before-stream-render", function(e) {
69
+ clearTimeout(timerId)
70
+ })
71
+ this.element.querySelector('form').requestSubmit()
72
+ // let url = `${this.input.dataset.url}?id=${this.elemId}`
73
+ // const form = document.createElement('form')
74
+ // form.setAttribute('method', 'post')
75
+ // form.setAttribute('action', url)
76
+ // form.setAttribute('data-turbo-stream', true)
77
+ // let partial = document.createElement('input')
78
+ // partial.setAttribute('type', 'hidden')
79
+ // partial.setAttribute('name', 'partial')
80
+ // partial.setAttribute('value', 'pg_associable/resultados_inline')
81
+ // form.appendChild(partial)
82
+ // let query = document.createElement('input')
83
+ // query.setAttribute('type', 'hidden')
84
+ // query.setAttribute('name', 'query')
85
+ // query.setAttribute('value', this.input.value)
86
+ // form.appendChild(query)
87
+ // document.body.prepend(form)
88
+ // form.requestSubmit()
89
+ // form.remove()
90
+ }
91
+
92
+ selectItem (e) {
93
+ const asociable = this.asociableOutlet
94
+ // TODO: text en data
95
+ asociable.completarCampo(e.target.dataset.id, e.target.text)
96
+ this.remove()
97
+ }
98
+
99
+ responseTargetConnected (e) {
100
+ const newObject = JSON.parse(e.dataset.response)
101
+ const asociable = this.asociableOutlet
102
+ asociable.completarCampo(newObject.id, newObject.to_s)
103
+ this.remove()
104
+ }
105
+
106
+ remove () {
107
+ this.targets.element.remove()
108
+ }
109
+
110
+ openModal () {
111
+ this.modalPuntero.show()
112
+ }
113
+
114
+ toggleCrearElegir (e) {
115
+ const content = this.element.querySelector('.modal-content')
116
+ const state = content.dataset.state
117
+ const newValue = state === 'new-item' ? 'select-item' : 'new-item'
118
+ content.setAttribute('data-state', newValue)
119
+ }
120
+
121
+ disconnect (e) {
122
+ console.log('ModalController disconnected')
123
+ this.modalPuntero.dispose()
124
+ document.querySelectorAll('.modal-backdrop').forEach(e => { e.remove() })
125
+ }
126
+
127
+ buscar () {
128
+ alert('buscar')
129
+ }
130
+ }
@@ -0,0 +1,31 @@
1
+ module PgAssociable
2
+ module FormBuilderMethods
3
+ def self.included(mod)
4
+ mod.include Rails.application.routes.url_helpers
5
+ mod.include PgEngine::RouteHelper
6
+ end
7
+
8
+ def pg_associable(atributo, options = {})
9
+ url_modal = namespaced_path(clase_asociacion(atributo), prefix: :abrir_modal)
10
+ options[:as] = 'pg_associable/pg_associable'
11
+ options[:wrapper] = :pg_associable
12
+ options[:url_modal] = url_modal
13
+ association atributo, options
14
+ end
15
+
16
+ def pg_associable_inline(atributo, options = {})
17
+ url_search = namespaced_path(clase_asociacion(atributo), prefix: :buscar)
18
+ options[:as] = 'pg_associable/pg_associable_inline'
19
+ options[:wrapper] = :pg_associable_inline
20
+ options[:url_search] = url_search
21
+ association atributo, options
22
+ end
23
+
24
+ def clase_asociacion(atributo)
25
+ asociacion = object.class.reflect_on_all_associations.find { |a| a.name == atributo.to_sym }
26
+ nombre_clase = asociacion.options[:class_name]
27
+ nombre_clase = asociacion.name.to_s.camelize if nombre_clase.nil?
28
+ Object.const_get(nombre_clase)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ module PgAssociable
2
+ module Helpers
3
+ def pg_respond_abrir_modal
4
+ respond_to do |format|
5
+ format.turbo_stream do
6
+ render turbo_stream: turbo_stream.append_all('body', partial: 'pg_associable_modal')
7
+ end
8
+ end
9
+ end
10
+
11
+ def pg_respond_buscar
12
+ partial = params[:partial] || 'pg_associable/resultados'
13
+ collection = @clase_modelo.query(params[:query]).limit(6)
14
+ render turbo_stream:
15
+ turbo_stream.update("resultados-#{params[:id]}",
16
+ partial:, locals: { collection: })
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgAssociable
4
+ class PgAssociableInlineInput < SimpleForm::Inputs::StringInput
5
+ include ActionView::Helpers::FormTagHelper
6
+
7
+ def hidden_input(wrapper_options = {})
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+ # merged_input_options = merge_wrapper_options(merged_input_options, { class: 'oculto' })
10
+ @builder.hidden_field(attribute_name, merged_input_options)
11
+ end
12
+
13
+ def search_form(wrapper_options = nil)
14
+ unless string?
15
+ input_html_classes.unshift('string')
16
+ # input_html_options[:type] ||= input_type if html5?
17
+ end
18
+ input_html_options[:type] = 'text'
19
+ input_html_options[:data] = { url: options[:url_search] }
20
+ input_html_options[:class] = 'form-control'
21
+ input_html_options[:placeholder] = ''
22
+
23
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
24
+
25
+ text_field_tag(nil, object.send(reflection.name).to_s, merged_input_options)
26
+ end
27
+
28
+ def limpiar(_wrapper_options = nil)
29
+ content_tag('a', href: 'javascript:void(0)', class: 'limpiar', title: 'Limpiar', tabindex: '0',
30
+ data: { action: 'asociable_inline#selectItem' }) do
31
+ '<i class="bi bi-x-lg"></i>'.html_safe
32
+ end
33
+ end
34
+
35
+ def pencil(_wrapper_options = nil)
36
+ '<i tabindex="-1" class="bi bi-pencil pencil"></i>'.html_safe
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PgAssociable
4
+ class PgAssociableInput < SimpleForm::Inputs::StringInput
5
+ include ActionView::Helpers::FormTagHelper
6
+
7
+ def hidden_input(wrapper_options = {})
8
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
9
+ # merged_input_options = merge_wrapper_options(merged_input_options, { class: 'oculto' })
10
+ @builder.hidden_field(attribute_name, merged_input_options)
11
+ end
12
+
13
+ def input(wrapper_options = nil)
14
+ unless string?
15
+ input_html_classes.unshift('string')
16
+ # input_html_options[:type] ||= input_type if html5?
17
+ end
18
+ input_html_options[:type] = 'text'
19
+
20
+ merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
21
+ merged_input_options = merge_wrapper_options(merged_input_options, { class: 'keep-disabled' })
22
+
23
+ text_field_tag(nil, object.send(reflection.name).to_s, merged_input_options)
24
+ end
25
+
26
+ def modal_link(_wrapper_options = nil)
27
+ "<a href=\"#{options[:url_modal]}\" class=\"modal-link d-inline-block\" data-turbo-stream style=\"position:absolute; left:0; right:0; top:0; bottom:0;\"></a>".html_safe
28
+ end
29
+
30
+ def limpiar(_wrapper_options = nil)
31
+ content_tag('a', href: 'javascript:void(0)', class: 'limpiar', title: 'Limpiar', tabindex: '0',
32
+ data: { action: 'asociable#selectItem' }) do
33
+ '<i class="bi bi-x-lg"></i>'.html_safe
34
+ end
35
+ end
36
+
37
+ def pencil(_wrapper_options = nil)
38
+ '<i tabindex="-1" class="bi bi-pencil pencil"></i>'.html_safe
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,9 @@
1
+ .resultados tabindex="-1"
2
+ - if collection.any?
3
+ .list-group.list-group-flush
4
+ - collection.each do |object|
5
+ = link_to object.to_s, 'javascript:void(0)',
6
+ class: 'list-group-item', data: { action: 'modal#selectItem', id: object.id }
7
+ - else
8
+ ul.list-group.list-group-flush
9
+ li.list-group-item No hay resultados para "#{params[:query]}"
@@ -0,0 +1,12 @@
1
+ .resultados tabindex="-1"
2
+ - if collection.any?
3
+ .list-group.list-group-flush
4
+ = link_to 'Ninguno', 'javascript:void(0)',
5
+ class: 'list-group-item', data: { action: 'asociable_inline#selectItem' }
6
+ - collection.each do |object|
7
+ = link_to object.to_s, 'javascript:void(0)',
8
+ class: 'list-group-item',
9
+ data: { action: 'asociable_inline#selectItem', id: object.id }
10
+ - else
11
+ ul.list-group.list-group-flush
12
+ li.list-group-item No hay resultados para "#{params[:query]}"
@@ -0,0 +1,39 @@
1
+ .modal.modal-asociable[class="modal-#{params[:id]}"
2
+ tabindex="-1" data-controller="modal"
3
+ data-modal-asociable-outlet=".asociable-#{params[:id]}"]
4
+ .modal-dialog
5
+ .modal-content.pg_associable data-state="select-item"
6
+ .modal-header
7
+ .modal-title.show-on-new-item
8
+ h3 Nuevo #{@clase_modelo.nombre_singular.downcase}
9
+ .modal-title.show-on-select-item
10
+ h3 Buscar #{@clase_modelo.nombre_singular.downcase}
11
+ a.btn-close[type="button" data-bs-dismiss="modal" aria-label="Close"]
12
+ .modal-body
13
+ .show-on-select-item
14
+ - if @clase_modelo.count.zero?
15
+ p No hay #{@clase_modelo.nombre_plural.downcase} aún
16
+ = link_to "Crear el primer #{@clase_modelo.nombre_singular.downcase}",
17
+ 'javascript:void(0)', data: { action: 'modal#toggleCrearElegir' }
18
+ - else
19
+ = form_tag namespaced_path(@clase_modelo, prefix: :buscar),
20
+ method: :post, class: 'pg-form buscar' do
21
+ = hidden_field_tag :id, params[:id]
22
+ = text_field_tag :query, '', class: 'form-control',
23
+ placeholder: 'Escribí algo para buscar',
24
+ autocomplete: 'off'
25
+ / = button_tag 'Buscar'
26
+ .resultados-wrapper id="resultados-#{params[:id]}" data-modal-target="result"
27
+ #pg-associable-form.show-on-new-item
28
+ = render partial: 'form', locals: { object: @clase_modelo.new, asociable: true }
29
+
30
+ .modal-footer
31
+ = link_to "Buscar #{@clase_modelo.nombre_singular.downcase} existente",
32
+ 'javascript:void(0)', class: 'btn btn-primary show-on-new-item',
33
+ data: { action: 'modal#toggleCrearElegir' }
34
+ = link_to "Nuevo #{@clase_modelo.nombre_singular.downcase}",
35
+ 'javascript:void(0)', class: 'btn btn-primary show-on-select-item',
36
+ data: { action: 'modal#toggleCrearElegir' }
37
+ = link_to "Sin #{@clase_modelo.nombre_singular.downcase}",
38
+ 'javascript:void(0)', class: 'btn btn-secondary',
39
+ data: { action: 'modal#selectItem' }
@@ -0,0 +1,7 @@
1
+ import AsociableInlineController from './app/assets/js/asociable_inline_controller'
2
+ import AsociableController from './app/assets/js/asociable_controller'
3
+ import ModalController from './app/assets/js/modal_controller'
4
+
5
+ window.Stimulus.register('asociable_inline', AsociableInlineController)
6
+ window.Stimulus.register('asociable', AsociableController)
7
+ window.Stimulus.register('modal', ModalController)
@@ -0,0 +1,12 @@
1
+ require "#{__dir__}/../../app/helpers/pg_associable/helpers"
2
+ require "#{__dir__}/../../app/helpers/pg_associable/form_builder_methods"
3
+
4
+ module PgAssociable
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace PgAssociable
7
+ # initializer 'configurar_simple_form', after: 'factory_bot.set_fixture_replacement' do
8
+ initializer 'configurar_pg_scaffold' do
9
+ require 'pg_associable/simple_form_initializer'
10
+ end
11
+ end
12
+ end