pg_rails 7.0.8.pre.alpha → 7.0.8.pre.alpha.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -0
- data/pg_associable/app/assets/stylesheets/pg_associable.scss +39 -40
- data/pg_associable/app/helpers/pg_associable/form_builder_methods.rb +45 -1
- data/pg_associable/app/helpers/pg_associable/helpers.rb +6 -4
- data/pg_associable/app/javascript/asociable_controller.tsx +96 -20
- data/pg_associable/app/javascript/modal_controller.js +4 -95
- data/pg_associable/app/views/pg_associable/_resultados_inline.html.slim +6 -8
- data/pg_associable/app/views/pg_engine/base/_pg_associable_modal.html.slim +5 -32
- data/pg_engine/app/assets/stylesheets/pg_rails_b5.scss +49 -41
- data/pg_engine/app/controllers/pg_engine/resource_helper.rb +6 -2
- data/pg_engine/app/decorators/pg_engine/base_decorator.rb +2 -2
- data/pg_engine/app/helpers/pg_engine/flash_helper.rb +2 -2
- data/pg_engine/app/helpers/pg_engine/form_helper.rb +70 -0
- data/pg_engine/app/helpers/pg_engine/route_helper.rb +14 -6
- data/pg_engine/app/views/admin/accounts/_form.html.slim +1 -1
- data/pg_engine/app/views/admin/user_accounts/_form.html.slim +1 -1
- data/pg_engine/app/views/admin/users/_form.html.slim +1 -1
- data/pg_engine/app/views/pg_engine/base/index.html.slim +3 -3
- data/pg_engine/config/simple_form/simple_form_bootstrap.rb +17 -9
- data/pg_engine/db/migrate/20240222115722_create_active_storage_tables.active_storage.rb +57 -0
- data/pg_engine/lib/pg_engine/engine.rb +1 -1
- data/pg_engine/lib/pg_engine/route_helpers.rb +1 -0
- data/pg_engine/lib/pg_engine/utils/pdf_preview_generator.rb +50 -0
- data/pg_engine/lib/pg_engine.rb +3 -0
- data/pg_engine/spec/fixtures/test.pdf +0 -0
- data/pg_engine/spec/pg_engine/pdf_preview_generator_spec.rb +12 -0
- data/pg_layout/app/assets/stylesheets/sidebar.scss +10 -2
- data/pg_layout/app/javascript/nested_controller.js +48 -0
- data/pg_layout/app/javascript/pg_form_controller.js +13 -0
- data/pg_layout/app/javascript/utils.ts +34 -0
- data/pg_layout/app/views/layouts/pg_layout/devise.html.slim +1 -1
- data/pg_layout/app/views/layouts/pg_layout/layout.html.slim +14 -11
- data/pg_layout/app/views/pg_layout/_flash.html.slim +1 -1
- data/pg_layout/app/views/pg_layout/_navbar.html.erb +10 -0
- data/pg_layout/app/views/pg_layout/_sidebar.html.erb +3 -3
- data/pg_layout/index.js +4 -0
- data/pg_rails/lib/version.rb +1 -1
- data/pg_scaffold/lib/generators/pg_slim/templates/_form.html.slim +2 -2
- metadata +9 -3
- data/pg_associable/app/views/pg_associable/_resultados.html.slim +0 -11
@@ -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
|
-
|
12
|
-
|
15
|
+
input[disabled] {
|
16
|
+
background-color: #e9e9ed;
|
17
|
+
color: black;
|
18
|
+
}
|
13
19
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
}
|
20
|
+
.form-control,
|
21
|
+
.form-select {
|
22
|
+
border: 1px solid #a7b7bb;
|
18
23
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
}
|
32
|
+
input[type=date] {
|
33
|
+
max-width: 12em;
|
34
|
+
}
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
}
|
36
|
+
input[type=datetime-local], input[type=datetime] {
|
37
|
+
max-width: 15em;
|
38
|
+
}
|
35
39
|
|
36
|
-
|
37
|
-
|
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
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
63
|
+
max-width: 17em;
|
64
|
+
}
|
70
65
|
|
71
|
-
|
72
|
-
|
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
|
-
|
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.
|
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(
|
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
|
@@ -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,
|
35
|
+
def namespaced_path(object, options = {})
|
36
36
|
target = [pg_namespace, object]
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
@@ -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.
|
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
|
|
@@ -46,7 +46,7 @@ SimpleForm.setup do |config|
|
|
46
46
|
# vertical forms
|
47
47
|
#
|
48
48
|
# vertical default_wrapper
|
49
|
-
|
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'
|
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
|
@@ -14,7 +14,7 @@ module PgEngine
|
|
14
14
|
end
|
15
15
|
|
16
16
|
if Rails.env.local?
|
17
|
-
initializer '
|
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"
|
@@ -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
|
data/pg_engine/lib/pg_engine.rb
CHANGED
@@ -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
|
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:
|
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
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
connect () {
|
5
|
+
this.element.querySelectorAll('.form-select, .form-control').forEach((slct) => {
|
6
|
+
slct.addEventListener('change', (e) => {
|
7
|
+
if (e.target.value) {
|
8
|
+
slct.classList.remove('is-invalid')
|
9
|
+
}
|
10
|
+
})
|
11
|
+
})
|
12
|
+
}
|
13
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
export function round (value) {
|
2
|
+
return Math.round(value * 100) / 100
|
3
|
+
}
|
4
|
+
|
5
|
+
export function printCurrency (value, moneda) {
|
6
|
+
return monedaSimbolo(moneda) + numberWithDots(round(value).toFixed(2).replace('.', ','))
|
7
|
+
}
|
8
|
+
|
9
|
+
export function showPercentage (value) {
|
10
|
+
return '% ' + value
|
11
|
+
}
|
12
|
+
|
13
|
+
export function monedaSimbolo (moneda) {
|
14
|
+
switch (moneda) {
|
15
|
+
case 'pesos':
|
16
|
+
return '$ '
|
17
|
+
case 'dolares':
|
18
|
+
return 'U$S '
|
19
|
+
case 'euros':
|
20
|
+
return '€ '
|
21
|
+
case 'reales':
|
22
|
+
return 'R$ '
|
23
|
+
case 'pesos_chilenos':
|
24
|
+
return 'CLP '
|
25
|
+
case 'pesos_mexicanos':
|
26
|
+
return 'MXN '
|
27
|
+
default:
|
28
|
+
return '$ '
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
export function numberWithDots (x) {
|
33
|
+
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.')
|
34
|
+
}
|