pg_rails 7.0.7 → 7.0.8.pre.alpha.6

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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -0
  3. data/pg_associable/app/assets/{css → stylesheets}/pg_associable.scss +40 -41
  4. data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +36 -9
  5. data/pg_associable/app/helpers/pg_associable/helpers.rb +6 -3
  6. data/pg_associable/app/inputs/pg_associable_input.rb +53 -0
  7. data/pg_associable/app/javascript/asociable_controller.tsx +247 -0
  8. data/pg_associable/app/javascript/modal_controller.js +29 -0
  9. data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +9 -8
  10. data/pg_associable/app/views/pg_engine/base/_pg_associable_modal.html.slim +5 -32
  11. data/pg_associable/index.js +2 -4
  12. data/pg_associable/lib/pg_associable/engine.rb +0 -4
  13. data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +49 -41
  14. data/pg_engine/app/controllers/pg_engine/resource_helper.rb +6 -2
  15. data/pg_engine/app/decorators/pg_engine/base_decorator.rb +2 -2
  16. data/pg_engine/app/helpers/pg_engine/flash_helper.rb +2 -2
  17. data/pg_engine/app/helpers/pg_engine/form_helper.rb +70 -0
  18. data/pg_engine/app/helpers/pg_engine/route_helper.rb +14 -6
  19. data/pg_engine/app/views/admin/accounts/_form.html.slim +1 -1
  20. data/pg_engine/app/views/admin/user_accounts/_form.html.slim +3 -3
  21. data/pg_engine/app/views/admin/users/_form.html.slim +1 -1
  22. data/pg_engine/app/views/pg_engine/base/index.html.slim +3 -3
  23. data/pg_engine/config/initializers/active_admin.rb +1 -4
  24. data/pg_engine/config/simple_form/simple_form_bootstrap.rb +17 -9
  25. data/pg_engine/db/migrate/20240222115722_create_active_storage_tables.active_storage.rb +57 -0
  26. data/pg_engine/db/seeds.rb +7 -4
  27. data/pg_engine/lib/pg_engine/engine.rb +1 -1
  28. data/pg_engine/lib/pg_engine/route_helpers.rb +1 -0
  29. data/pg_engine/lib/pg_engine/utils/pdf_preview_generator.rb +50 -0
  30. data/pg_engine/lib/pg_engine.rb +3 -0
  31. data/pg_engine/lib/tasks/auto_anotar_modelos.rake +1 -1
  32. data/pg_engine/spec/fixtures/test.pdf +0 -0
  33. data/pg_engine/spec/pg_engine/pdf_preview_generator_spec.rb +12 -0
  34. data/pg_layout/app/assets/stylesheets/sidebar.scss +10 -2
  35. data/pg_layout/app/javascript/nested_controller.js +48 -0
  36. data/pg_layout/app/javascript/pg_form_controller.js +13 -0
  37. data/pg_layout/app/javascript/utils.ts +34 -0
  38. data/pg_layout/app/views/layouts/pg_layout/devise.html.slim +1 -1
  39. data/pg_layout/app/views/layouts/pg_layout/layout.html.slim +14 -10
  40. data/pg_layout/app/views/pg_layout/_flash.html.slim +2 -2
  41. data/pg_layout/app/views/pg_layout/_navbar.html.erb +10 -0
  42. data/pg_layout/app/views/pg_layout/_sidebar.html.erb +3 -3
  43. data/pg_layout/index.js +4 -0
  44. data/pg_rails/lib/version.rb +1 -1
  45. data/pg_rails/scss/pg_rails.scss +1 -1
  46. data/pg_scaffold/lib/generators/pg_slim/templates/_form.html.slim +3 -3
  47. metadata +15 -13
  48. data/pg_associable/app/assets/js/asociable_controller.js +0 -58
  49. data/pg_associable/app/assets/js/asociable_inline_controller.js +0 -142
  50. data/pg_associable/app/assets/js/modal_controller.js +0 -117
  51. data/pg_associable/app/inputs/pg_associable/pg_associable_inline_input.rb +0 -39
  52. data/pg_associable/app/inputs/pg_associable/pg_associable_input.rb +0 -41
  53. data/pg_associable/app/views/pg_associable/_resultados.html.slim +0 -9
  54. data/pg_associable/lib/pg_associable/simple_form_initializer.rb +0 -34
  55. data/pg_associable/lib/tasks/pg_associable_tasks.rake +0 -4
@@ -6,48 +6,44 @@
6
6
  --bs-form-invalid-border-color: #b50000;
7
7
  }
8
8
 
9
+ // Toasts
10
+ .toast.show {
11
+ display: inline-block;
12
+ }
9
13
 
10
14
  // FORMS
11
- // .pg-form {
12
- // max-width: 300px;
15
+ input[disabled] {
16
+ background-color: #e9e9ed;
17
+ color: black;
18
+ }
13
19
 
14
- input[disabled] {
15
- background-color: #e9e9ed;
16
- color: black;
17
- }
20
+ .form-control,
21
+ .form-select {
22
+ border: 1px solid #a7b7bb;
18
23
 
19
- .form-control,
20
- .form-select {
21
- width: 100%;
22
- height: 35px;
23
- border: 1px solid #a7b7bb;
24
- border-radius: 3px;
24
+ &:focus, &:focus-visible {
25
+ outline: 1px solid color.adjust(#a7b7bb, $saturation: +30%);
26
+ }
27
+ }
28
+ select[multiple] {
29
+ height: inherit;
30
+ }
25
31
 
26
- &.form-control-sm,
27
- &.form-select-sm {
28
- height: 29px;
29
- }
32
+ input[type=date] {
33
+ max-width: 12em;
34
+ }
30
35
 
31
- &.is-invalid {
32
- background-image: none!important; /* override bootstrap forms */
33
- padding-right: 0; /* override bootstrap forms */
34
- }
36
+ input[type=datetime-local], input[type=datetime] {
37
+ max-width: 15em;
38
+ }
35
39
 
36
- &:focus, &:focus-visible {
37
- outline: 1px solid color.adjust(#a7b7bb, $saturation: +30%);
38
- }
39
- }
40
- select[multiple] {
41
- height: inherit;
42
- }
43
- .form-control:focus {
44
- box-shadow: none!important; /* override bootstrap forms */
45
- }
40
+ .form-select:not(:has(option)) {
41
+ background-color: #d5d5d5;
42
+ }
46
43
 
47
- input[type=date] {
48
- max-width: 170px;
49
- }
50
- // }
44
+ .form-control.is-invalid, .form-select.is-invalid {
45
+ background-color: #fff3f3;
46
+ }
51
47
 
52
48
  // LISTADOS
53
49
  .listado {
@@ -56,9 +52,7 @@
56
52
  }
57
53
  }
58
54
  .listado .btn-sm {
59
- min-width: 25px;
60
- height: 25px;
61
- padding: 1px;
55
+ padding: 0em 0.3em;
62
56
  margin-right: 4px;
63
57
  }
64
58
 
@@ -66,9 +60,23 @@
66
60
  .filter {
67
61
  display: inline-block;
68
62
  vertical-align: top;
69
- min-width: 200px;
63
+ max-width: 17em;
64
+ }
70
65
 
71
- .input-group {
72
- width: 230px;
73
- }
66
+ // Modal
67
+ .modal-content {
68
+ box-shadow: 15px 15px 9px 0px rgba(0, 0, 0, 0.6);
69
+ }
70
+
71
+ // Nested
72
+ .link-to-add:hover {
73
+ background-color: #f4f4f4;
74
+ }
75
+
76
+ .link-to-add {
77
+ display: inline-block;
78
+ width: 100%;
79
+ text-align: center;
80
+ border-radius: var(--bs-border-radius);
81
+ padding: 0.6em 0.4em;
74
82
  }
@@ -93,10 +93,12 @@ module PgEngine
93
93
  if params[:asociable]
94
94
  format.turbo_stream do
95
95
  render turbo_stream:
96
- turbo_stream.update('pg-associable-form', <<~HTML
96
+ # rubocop:disable Rails/SkipsModelValidations
97
+ turbo_stream.update_all('.modal.show .pg-associable-form', <<~HTML
97
98
  <div data-modal-target="response" data-response='#{object.decorate.to_json}'></div>
98
99
  HTML
99
100
  )
101
+ # rubocop:enable Rails/SkipsModelValidations
100
102
  end
101
103
  end
102
104
  format.html do
@@ -113,8 +115,10 @@ module PgEngine
113
115
  # self.instancia_modelo = instancia_modelo.decorate
114
116
  if params[:asociable]
115
117
  format.turbo_stream do
118
+ # rubocop:disable Rails/SkipsModelValidations
116
119
  render turbo_stream:
117
- turbo_stream.update('pg-associable-form', partial: 'form', locals: { asociable: true })
120
+ turbo_stream.update_all('.modal.show .pg-associable-form', partial: 'form', locals: { asociable: true })
121
+ # rubocop:enable Rails/SkipsModelValidations
118
122
  end
119
123
  end
120
124
  format.html { render :new, status: :unprocessable_entity }
@@ -12,8 +12,8 @@ module PgEngine
12
12
 
13
13
  delegate_all
14
14
 
15
- def as_json(_options = {})
16
- object.as_json.tap { |o| o[:to_s] = to_s }
15
+ def as_json(options = {})
16
+ object.as_json(options).tap { |o| o[:to_s] = to_s }
17
17
  end
18
18
 
19
19
  # rubocop:disable Style/MissingRespondToMissing
@@ -14,9 +14,9 @@ module PgEngine
14
14
  case flash_type
15
15
  when 'notice'
16
16
  'info'
17
- when 'error'
18
- 'danger'
19
17
  when 'alert'
18
+ 'danger'
19
+ when 'warning'
20
20
  'warning'
21
21
  when 'success'
22
22
  'success'
@@ -29,5 +29,75 @@ module PgEngine
29
29
  uri.path = "#{uri.path}.#{formato}"
30
30
  uri.to_s
31
31
  end
32
+
33
+ # This method creates a link with `data-id` `data-fields` attributes.
34
+ # These attributes are used to create new instances of the nested fields through Javascript.
35
+ def link_to_add_fields(name, form, association, required: false)
36
+ # Takes an object (@person) and creates a new instance of its associated model (:addresses)
37
+ # To better understand, run the following in your terminal:
38
+ # rails c --sandbox
39
+ # @person = Person.new
40
+ # new_object = @person.send(:addresses).klass.new
41
+ new_object = form.object.send(association).klass.new
42
+
43
+ # Saves the unique ID of the object into a variable.
44
+ # This is needed to ensure the key of the associated array is unique.
45
+ # This is makes parsing the content in the `data-fields` attribute easier through Javascript.
46
+ # We could use another method to achive this.
47
+ id = new_object.object_id
48
+
49
+ # https://api.rubyonrails.org/ fields_for(record_name, record_object = nil, fields_options = {}, &block)
50
+ # record_name = :addresses
51
+ # record_object = new_object
52
+ # fields_options = { child_index: id }
53
+ # child_index` is used to ensure the key of the associated array is unique,
54
+ # and that it matched the value in the `data-id` attribute.
55
+ # `person[addresses_attributes][child_index_value][_destroy]`
56
+ fields =
57
+ form.fields_for(association, new_object, child_index: id) do |builder|
58
+ # `association.to_s.singularize + "_fields"` ends up evaluating to `address_fields`
59
+ # The render function will then look for `views/people/_address_fields.html.erb`
60
+ # The render function also needs to be passed the value of 'builder', because
61
+ # `views/people/_address_fields.html.erb` needs this to render the form tags.
62
+ render("#{association.to_s.singularize}_fields", f: builder)
63
+ end
64
+
65
+ # This renders a simple link, but passes information into `data` attributes.
66
+ # This info can be named anything we want, but in this case we chose `data-id:` and `data-fields:`.
67
+ # The `id:` is from `new_object.object_id`.
68
+ # The `fields:` are rendered from the `fields` blocks.
69
+ # We use `gsub("\n", "")` to remove anywhite space from the rendered partial.
70
+ # The `id:` value needs to match the value used in `child_index: id`.
71
+ link_to(
72
+ 'javascript:void(0)',
73
+ class: 'link-to-add',
74
+ data: {
75
+ controller: 'nested',
76
+ action: 'nested#addItem',
77
+ id:,
78
+ required:,
79
+ fields: fields.gsub("\n", '')
80
+ }
81
+ ) do
82
+ # rubocop:disable Rails/OutputSafety
83
+ "<i class=\"bi bi-plus-lg\"></i> #{name}".html_safe
84
+ # rubocop:enable Rails/OutputSafety
85
+ end
86
+ end
87
+
88
+ def link_to_remove(text = nil, &)
89
+ if block_given?
90
+ link_to('javascript:void(0)', class: 'link-to-remove text-danger-emphasis', title: 'Quitar',
91
+ data: { controller: 'nested', action: 'nested#quitar' }, &)
92
+ elsif text.present?
93
+ link_to text, 'javascript:void(0)', class: 'link-to-remove text-danger-emphasis', title: 'Quitar',
94
+ data: { controller: 'nested', action: 'nested#quitar' }
95
+ else
96
+ link_to 'javascript:void(0)', class: 'link-to-remove text-danger-emphasis', title: 'Quitar',
97
+ data: { controller: 'nested', action: 'nested#quitar' } do
98
+ '<i class="bi bi-x-lg"></i>'.html_safe
99
+ end
100
+ end
101
+ end
32
102
  end
33
103
  end
@@ -18,7 +18,7 @@ module PgEngine
18
18
 
19
19
  def self.namespace(context)
20
20
  req = request(context)
21
- route = Rails.application.routes.recognize_path(req.path)
21
+ route = Rails.application.routes.recognize_path(req.path, method: req.env['REQUEST_METHOD'])
22
22
  parts = route[:controller].split('/')
23
23
  return unless parts.length > 1
24
24
 
@@ -32,12 +32,20 @@ module PgEngine
32
32
  NamespaceDeductor.namespace(self)
33
33
  end
34
34
 
35
- def namespaced_path(object, prefix: nil, suffix: nil)
35
+ def namespaced_path(object, options = {})
36
36
  target = [pg_namespace, object]
37
- target.prepend prefix if prefix
38
- target.append suffix if suffix
39
- target = target.flatten.compact
40
- polymorphic_url(target, only_path: true)
37
+
38
+ if options[:prefix]
39
+ target.prepend options[:prefix]
40
+ options.delete(:prefix)
41
+ end
42
+
43
+ if options[:suffix]
44
+ target.append options[:suffix]
45
+ options.delete(:suffix)
46
+ end
47
+
48
+ polymorphic_url(target.flatten.compact, options.merge(only_path: true))
41
49
  end
42
50
  end
43
51
  end
@@ -1,6 +1,6 @@
1
1
  / # locals: (object: nil, asociable: false)
2
2
 
3
- div style="max-width: 300px"
3
+ div style="max-width: 22em"
4
4
  = pg_form_for(@account || object) do |f|
5
5
  = f.mensajes_de_error
6
6
 
@@ -1,12 +1,12 @@
1
1
  / # locals: (object: nil, asociable: false)
2
2
 
3
- div style="max-width: 300px"
3
+ div style="max-width: 22em"
4
4
  = pg_form_for(@user_account || object) do |f|
5
5
  = f.mensajes_de_error
6
6
 
7
7
  = hidden_field_tag :asociable, true if asociable
8
- = f.pg_associable_inline :user
9
- = f.pg_associable_inline :account
8
+ = f.pg_associable :user
9
+ = f.pg_associable :account
10
10
  = f.input :profiles
11
11
  .mt-2
12
12
  = f.button :submit
@@ -1,6 +1,6 @@
1
1
  / # locals: (object: nil, asociable: false)
2
2
 
3
- div style="max-width: 300px"
3
+ div style="max-width: 22em"
4
4
  = pg_form_for(@user || object) do |f|
5
5
  = f.mensajes_de_error
6
6
 
@@ -9,8 +9,8 @@
9
9
  = @clase_modelo.new.decorate.new_link
10
10
  .ms-1
11
11
  = @clase_modelo.new.decorate.export_link(request.url)
12
- .collapse.p-2.border-bottom#filtros class="#{ 'show' if any_filter? }"
13
- .d-flex.align-items-center
12
+ .collapse.border-bottom#filtros class="#{ 'show' if any_filter? }"
13
+ .d-flex.align-items-center.p-2
14
14
  .px-2.d-none.d-sm-inline-block
15
15
  span.bi.bi-funnel-fill
16
16
  = form_tag nil, class: '', method: :get do
@@ -20,7 +20,7 @@
20
20
  = button_tag class: 'btn btn-sm btn-primary col-auto' do
21
21
  span.bi.bi-search
22
22
  .col-auto
23
- = link_to namespaced_path(@clase_modelo),
23
+ = link_to namespaced_path(@clase_modelo, clean: true),
24
24
  class: 'btn btn-sm btn-secondary col-auto' do
25
25
  | Limpiar
26
26
 
@@ -34,10 +34,7 @@ ActiveAdmin.setup do |config|
34
34
  # File.join(Rails.root, 'app', 'admin'),
35
35
  # File.join(Rails.root, 'app', 'cashier')
36
36
  # ]
37
- config.load_paths = [
38
- File.join(Rails.root, 'app', 'admin'),
39
- File.join(PgEngine::Engine.root, 'app', 'admin')
40
- ]
37
+ config.load_paths.push(File.join(PgEngine::Engine.root, 'app', 'admin'))
41
38
  # == Default Namespace
42
39
  #
43
40
  # Set the default namespace each administration resource
@@ -46,7 +46,7 @@ SimpleForm.setup do |config|
46
46
  # vertical forms
47
47
  #
48
48
  # vertical default_wrapper
49
- config.wrappers :vertical_form, class: 'mb-3' do |b|
49
+ control_wrapper = lambda do |b|
50
50
  b.use :html5
51
51
  b.use :placeholder
52
52
  b.optional :maxlength
@@ -60,6 +60,21 @@ SimpleForm.setup do |config|
60
60
  b.use :hint, wrap_with: { class: 'form-text' }
61
61
  end
62
62
 
63
+ select_wrapper = lambda do |b|
64
+ b.use :html5
65
+ b.optional :readonly
66
+ b.use :label, class: 'form-label'
67
+ b.use :input, class: 'form-select', error_class: 'is-invalid'
68
+ b.use :full_error, wrap_with: { class: 'invalid-feedback' }
69
+ b.use :hint, wrap_with: { class: 'form-text' }
70
+ end
71
+
72
+ config.wrappers :vertical_no_margin_control, &control_wrapper
73
+
74
+ config.wrappers :vertical_no_margin_select, &select_wrapper
75
+
76
+ config.wrappers :vertical_form, class: 'mb-3', &control_wrapper
77
+
63
78
  # vertical input for boolean
64
79
  config.wrappers :vertical_boolean, tag: 'fieldset', class: 'mb-3' do |b|
65
80
  b.use :html5
@@ -112,14 +127,7 @@ SimpleForm.setup do |config|
112
127
  end
113
128
 
114
129
  # vertical select input
115
- config.wrappers :vertical_select, class: 'mb-3' do |b|
116
- b.use :html5
117
- b.optional :readonly
118
- b.use :label, class: 'form-label'
119
- b.use :input, class: 'form-select', error_class: 'is-invalid'
120
- b.use :full_error, wrap_with: { class: 'invalid-feedback' }
121
- b.use :hint, wrap_with: { class: 'form-text' }
122
- end
130
+ config.wrappers :vertical_select, class: 'mb-3', &select_wrapper
123
131
 
124
132
  # vertical multi select
125
133
  config.wrappers :vertical_multi_select, class: 'mb-3' do |b|
@@ -0,0 +1,57 @@
1
+ # This migration comes from active_storage (originally 20170806125915)
2
+ class CreateActiveStorageTables < ActiveRecord::Migration[7.0]
3
+ def change
4
+ # Use Active Record's configured type for primary and foreign keys
5
+ primary_key_type, foreign_key_type = primary_and_foreign_key_types
6
+
7
+ create_table :active_storage_blobs, id: primary_key_type do |t|
8
+ t.string :key, null: false
9
+ t.string :filename, null: false
10
+ t.string :content_type
11
+ t.text :metadata
12
+ t.string :service_name, null: false
13
+ t.bigint :byte_size, null: false
14
+ t.string :checksum
15
+
16
+ if connection.supports_datetime_with_precision?
17
+ t.datetime :created_at, precision: 6, null: false
18
+ else
19
+ t.datetime :created_at, null: false
20
+ end
21
+
22
+ t.index [ :key ], unique: true
23
+ end
24
+
25
+ create_table :active_storage_attachments, id: primary_key_type do |t|
26
+ t.string :name, null: false
27
+ t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28
+ t.references :blob, null: false, type: foreign_key_type
29
+
30
+ if connection.supports_datetime_with_precision?
31
+ t.datetime :created_at, precision: 6, null: false
32
+ else
33
+ t.datetime :created_at, null: false
34
+ end
35
+
36
+ t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
37
+ t.foreign_key :active_storage_blobs, column: :blob_id
38
+ end
39
+
40
+ create_table :active_storage_variant_records, id: primary_key_type do |t|
41
+ t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42
+ t.string :variation_digest, null: false
43
+
44
+ t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
45
+ t.foreign_key :active_storage_blobs, column: :blob_id
46
+ end
47
+ end
48
+
49
+ private
50
+ def primary_and_foreign_key_types
51
+ config = Rails.configuration.generators
52
+ setting = config.options[config.orm][:primary_key_type]
53
+ primary_key_type = setting || :primary_key
54
+ foreign_key_type = setting || :bigint
55
+ [primary_key_type, foreign_key_type]
56
+ end
57
+ end
@@ -1,5 +1,8 @@
1
- ['mrosso10@gmail.com'].each do |mail|
2
- unless User.exists?(email: mail)
3
- User.create(email: mail, password: 'admin123', confirmed_at: Time.now, developer: true)
4
- end
1
+ DatabaseCleaner.clean_with(:truncation, except: %w(ar_internal_metadata users accounts user_accounts))
2
+
3
+ MAIL = 'mrosso10@gmail.com'
4
+
5
+ unless User.where(email: MAIL).exists?
6
+ FactoryBot.create :user, email: MAIL, password: 'admin123',
7
+ confirmed_at: Time.now, developer: true
5
8
  end
@@ -14,7 +14,7 @@ module PgEngine
14
14
  end
15
15
 
16
16
  if Rails.env.local?
17
- initializer 'configurar_factories', after: 'factory_bot.set_factory_paths' do
17
+ initializer 'pg_engine.set_factory_paths', after: 'factory_bot.set_factory_paths' do
18
18
  # Para que tome las factories de pg_engine/spec/factories
19
19
  # además de las de dummy/spec/factories
20
20
  FactoryBot.definition_file_paths << "#{root}/spec/factories"
@@ -6,6 +6,7 @@ module PgEngine
6
6
  get :abrir_modal
7
7
  post :buscar
8
8
  end
9
+ yield if block_given?
9
10
  end
10
11
  end
11
12
  end
@@ -0,0 +1,50 @@
1
+ require 'English'
2
+
3
+ module PgEngine
4
+ class PdfPreviewGenerator
5
+ def open_tempfile
6
+ tempfile = Tempfile.open('PgEnginePdfPreview-', 'tmp')
7
+
8
+ begin
9
+ yield tempfile
10
+ ensure
11
+ tempfile.close
12
+ tempfile.unlink
13
+ end
14
+ end
15
+
16
+ def capture(*argv, to:)
17
+ to.binmode
18
+
19
+ open_tempfile do |err|
20
+ IO.popen(argv, err:) { |out| IO.copy_stream(out, to) }
21
+ err.rewind
22
+
23
+ unless $CHILD_STATUS.success?
24
+ raise "#{argv.first} failed (status #{$CHILD_STATUS.exitstatus}): #{err.read.to_s.chomp}"
25
+ end
26
+ end
27
+
28
+ to.rewind
29
+ end
30
+
31
+ def draw(*argv)
32
+ open_tempfile do |file|
33
+ capture(*argv, to: file)
34
+
35
+ yield file
36
+ end
37
+ end
38
+
39
+ def run(pdf_string)
40
+ open_tempfile do |tmp_pdf_file|
41
+ tmp_pdf_file.binmode
42
+ tmp_pdf_file.write pdf_string
43
+ tmp_pdf_file.close
44
+ draw 'pdftoppm', '-singlefile', '-cropbox', '-r', '72', '-png', tmp_pdf_file.path do |file|
45
+ return file.read
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -5,6 +5,9 @@ require_relative 'pg_engine/core_ext'
5
5
  require_relative 'pg_engine/configuracion'
6
6
  require_relative 'pg_engine/route_helpers'
7
7
  require_relative 'pg_engine/utils/pg_logger'
8
+ require_relative 'pg_engine/utils/pdf_preview_generator'
9
+
10
+ require_relative '../app/helpers/pg_engine/print_helper'
8
11
 
9
12
  module PgEngine
10
13
  class << self
@@ -11,7 +11,7 @@ if Rails.env.development?
11
11
  'additional_file_patterns' => [],
12
12
  'routes' => 'true',
13
13
  'models' => 'true',
14
- 'position_in_routes' => 'before',
14
+ 'position_in_routes' => 'after',
15
15
  'position_in_class' => 'before',
16
16
  'position_in_test' => 'before',
17
17
  'position_in_fixture' => 'before',
Binary file
@@ -0,0 +1,12 @@
1
+ require 'rails_helper'
2
+
3
+ describe PgEngine::PdfPreviewGenerator do
4
+ let(:pdf_string) { File.read("#{PgEngine::Engine.root}/spec/fixtures/test.pdf") }
5
+ let(:instancia) { described_class.new }
6
+
7
+ describe '#run' do
8
+ subject { instancia.run(pdf_string) }
9
+
10
+ it { is_expected.to be_a String }
11
+ end
12
+ end
@@ -54,6 +54,7 @@ $chevron-color: 200,200,200,.5;
54
54
  font-weight: bold;
55
55
  --bs-link-color-rgb: white!important;
56
56
  }
57
+ // Los small-items están deprecados
57
58
  #sidebar {
58
59
  &.opened {
59
60
  .sidebar--small-items {
@@ -68,7 +69,7 @@ $chevron-color: 200,200,200,.5;
68
69
  display: block;
69
70
  }
70
71
  .sidebar--large-items {
71
- display: none;
72
+ // display: none;
72
73
  }
73
74
  }
74
75
  }
@@ -84,11 +85,18 @@ $chevron-color: 200,200,200,.5;
84
85
  // display: none!important;
85
86
  flex-basis: 0px;
86
87
  transition: flex-basis 0.5s;
88
+
89
+ // Para que al cerrar y abrir no se vean los textos por fuera de la sidebar
90
+ clip-path: inset(0 0 0 0);
87
91
  // flex-grow: 1;
88
92
 
89
93
  &.opened {
90
94
  // display: block!important;
91
- flex-basis: 140px;
95
+ flex-basis: 9em;
96
+ }
97
+ & > * {
98
+ width: 9em;
99
+ position:fixed;
92
100
  }
93
101
  }
94
102
 
@@ -0,0 +1,48 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ connect () {
5
+ this.showRemove()
6
+ }
7
+
8
+ addItem () {
9
+ // Save a unique timestamp to ensure the key of the associated array is unique.
10
+ const time = new Date().getTime()
11
+ // Save the data id attribute into a variable. This corresponds to `new_object.object_id`.
12
+ const linkId = this.element.dataset.id
13
+ // Create a new regular expression needed to find any instance of the `new_object.object_id` used in the fields data attribute if there's a value in `linkId`.
14
+ const regexp = linkId ? new RegExp(linkId, 'g') : null
15
+ // Replace all instances of the `new_object.object_id` with `time`, and save markup into a variable if there's a value in `regexp`.
16
+ const newFields = regexp ? this.element.dataset.fields.replace(regexp, time) : null
17
+ // Add the new markup to the form if there are fields to add.
18
+ if (newFields) {
19
+ this.element.insertAdjacentHTML('beforebegin', newFields)
20
+ }
21
+ this.element.closest('form').dispatchEvent(new Event('nestedField:added'))
22
+ this.showRemove()
23
+ }
24
+
25
+ quitar () {
26
+ const parent = this.element.closest('.nested-fields')
27
+ parent.style.display = 'none'
28
+ parent.classList.add('removed')
29
+ parent.querySelector('[name*=destroy]').value = 'true'
30
+ this.element.closest('form').dispatchEvent(new Event('nestedField:removed'))
31
+ this.showRemove()
32
+ }
33
+
34
+ showRemove () {
35
+ const container = this.element.closest('.nested-container')
36
+ if (container.dataset.required === 'true') {
37
+ if (container.querySelectorAll('.nested-fields:not(.removed)').length === 1) {
38
+ container.querySelectorAll('.link-to-remove').forEach((e) => {
39
+ e.style.visibility = 'hidden'
40
+ })
41
+ } else {
42
+ container.querySelectorAll('.link-to-remove').forEach((e) => {
43
+ e.style.visibility = ''
44
+ })
45
+ }
46
+ }
47
+ }
48
+ }