headmin 0.5.3 → 0.5.5
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/app/assets/javascripts/headmin/controllers/media_controller.js +14 -14
- data/app/assets/javascripts/headmin/controllers/media_modal_controller.js +5 -5
- data/app/assets/javascripts/headmin/controllers/remote_modal_controller.js +1 -2
- data/app/assets/javascripts/headmin/controllers/textarea_controller.js +3 -3
- data/app/assets/javascripts/headmin.js +11 -11
- data/app/assets/stylesheets/headmin/forms/repeater.scss +0 -4
- data/app/assets/stylesheets/headmin/forms.scss +0 -6
- data/app/assets/stylesheets/headmin/overrides/redactorx.scss +1 -1
- data/app/assets/stylesheets/headmin/vendor/{tom-select-bootstrap.css → tom-select-bootstrap.scss} +0 -1
- data/app/assets/stylesheets/headmin.css +4 -8
- data/app/controllers/concerns/headmin/pagination.rb +5 -1
- data/app/models/concerns/headmin/field.rb +1 -1
- data/app/models/headmin/filter/association.rb +86 -0
- data/app/models/headmin/filter/association_view.rb +74 -0
- data/app/models/headmin/filter/base.rb +39 -19
- data/app/models/headmin/filter/boolean_view.rb +2 -5
- data/app/models/headmin/filter/date_view.rb +2 -5
- data/app/models/headmin/filter/field.rb +55 -0
- data/app/models/headmin/filter/field_view.rb +50 -0
- data/app/models/headmin/filter/filter_view.rb +25 -0
- data/app/models/headmin/filter/flatpickr_view.rb +1 -0
- data/app/models/headmin/filter/number_view.rb +2 -5
- data/app/models/headmin/filter/operator_view.rb +3 -1
- data/app/models/headmin/filter/options_view.rb +2 -5
- data/app/models/headmin/filter/text_view.rb +2 -5
- data/app/models/headmin/filters.rb +19 -4
- data/app/models/headmin/form/association_view.rb +102 -0
- data/app/models/headmin/form/blocks_view.rb +4 -1
- data/app/models/headmin/form/file_view.rb +0 -8
- data/app/models/headmin/form/flatpickr_view.rb +2 -1
- data/app/models/headmin/form/media_item_view.rb +39 -0
- data/app/models/headmin/form/media_view.rb +27 -3
- data/app/models/headmin/form/select_view.rb +2 -1
- data/app/models/headmin/thumbnail_view.rb +40 -19
- data/app/models/view_model.rb +4 -0
- data/app/views/headmin/_filters.html.erb +7 -2
- data/app/views/headmin/_thumbnail.html.erb +32 -8
- data/app/views/headmin/filters/_association.html.erb +24 -0
- data/app/views/headmin/filters/_field.html.erb +23 -0
- data/app/views/headmin/filters/_options.html.erb +1 -1
- data/app/views/headmin/forms/_association.html.erb +30 -0
- data/app/views/headmin/forms/_file.html.erb +4 -5
- data/app/views/headmin/forms/_media.html.erb +7 -5
- data/app/views/headmin/forms/_repeater.html.erb +10 -8
- data/app/views/headmin/forms/_wrapper.html.erb +0 -1
- data/app/views/headmin/forms/fields/_list.html.erb +6 -4
- data/app/views/headmin/forms/media/_item.html.erb +18 -12
- data/app/views/headmin/forms/repeater/_row.html.erb +2 -1
- data/app/views/headmin/media/_item.html.erb +1 -2
- data/app/views/headmin/media/_media_item_modal.html.erb +2 -2
- data/app/views/headmin/table/body/_association.html.erb +17 -3
- data/app/views/headmin/views/devise/shared/_links.html.erb +14 -20
- data/config/locales/activerecord/en.yml +1 -0
- data/config/locales/activerecord/nl.yml +1 -0
- data/config/locales/headmin/filters/en.yml +3 -1
- data/config/locales/headmin/filters/nl.yml +2 -0
- data/config/locales/headmin/media/en.yml +2 -2
- data/config/locales/headmin/media/nl.yml +2 -1
- data/lib/headmin/version.rb +1 -1
- data/package.json +1 -1
- metadata +13 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3445263c38deb23953f15bd095f0ab4d35acdba03e57863990afc270b3b0ec2d
|
4
|
+
data.tar.gz: 7ff3cbb8b497e7abfec49ee1222f3980b08779627179d788cd332794076c3ec7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b6d3c45da2fc0d1e3d9d3f69daccfdb689eae2785cc46d19fdec80ffff497987193fcb251eb2576f95d2d7927179d2c0264fa0548c3363240193a0c51e7efcc
|
7
|
+
data.tar.gz: 1badf3f039045aaf9ddee10bcf1f2686bd3cd44f0e04db6f893c8e61c4cf8974b3464a427bcf6c66cbc7bfd03e2b502d48bd4edd8a4c26527484feefac74691b
|
data/Gemfile.lock
CHANGED
@@ -14,7 +14,7 @@ export default class extends Controller {
|
|
14
14
|
})
|
15
15
|
|
16
16
|
// Init sorting
|
17
|
-
if(this.hasSorting()) {
|
17
|
+
if (this.hasSorting()) {
|
18
18
|
this.initSortable()
|
19
19
|
}
|
20
20
|
|
@@ -46,7 +46,7 @@ export default class extends Controller {
|
|
46
46
|
}
|
47
47
|
|
48
48
|
// Methods
|
49
|
-
initSortable() {
|
49
|
+
initSortable () {
|
50
50
|
Sortable.create(this.thumbnailsTarget, {
|
51
51
|
handle: '.media-drag-sort-handle',
|
52
52
|
onEnd: (event) => {
|
@@ -55,8 +55,8 @@ export default class extends Controller {
|
|
55
55
|
})
|
56
56
|
}
|
57
57
|
|
58
|
-
hasSorting() {
|
59
|
-
return this.element.dataset.sort ===
|
58
|
+
hasSorting () {
|
59
|
+
return this.element.dataset.sort === 'true'
|
60
60
|
}
|
61
61
|
|
62
62
|
destroyItem (item) {
|
@@ -90,7 +90,7 @@ export default class extends Controller {
|
|
90
90
|
|
91
91
|
validate () {
|
92
92
|
this.clearValidation()
|
93
|
-
if (this.element.dataset.required ===
|
93
|
+
if (this.element.dataset.required === '0') return
|
94
94
|
this.validateMinimum()
|
95
95
|
this.validateMaximum()
|
96
96
|
}
|
@@ -162,7 +162,7 @@ export default class extends Controller {
|
|
162
162
|
}
|
163
163
|
|
164
164
|
enableItem (item) {
|
165
|
-
item.querySelector(
|
165
|
+
item.querySelector('input[name*=\'_destroy\']').value = false
|
166
166
|
item.classList.remove('d-none')
|
167
167
|
}
|
168
168
|
|
@@ -174,11 +174,11 @@ export default class extends Controller {
|
|
174
174
|
|
175
175
|
// Set new values
|
176
176
|
const newItem = this.itemTargets.pop()
|
177
|
-
newItem.querySelector(
|
178
|
-
newItem.querySelector(
|
177
|
+
newItem.querySelector('input[name*="[blob_id]"]').value = item.blobId
|
178
|
+
newItem.querySelector('input[name*="[_destroy]"]').value = false
|
179
179
|
|
180
180
|
// Update edit button url
|
181
|
-
const editButton = newItem.querySelector(
|
181
|
+
const editButton = newItem.querySelector('[data-media-target="editButton"]')
|
182
182
|
editButton.setAttribute('href', editButton.getAttribute('href').replace('$1', item.blobId))
|
183
183
|
|
184
184
|
// Copy thumbnail
|
@@ -204,7 +204,7 @@ export default class extends Controller {
|
|
204
204
|
}
|
205
205
|
|
206
206
|
removeItem (item) {
|
207
|
-
item.querySelector(
|
207
|
+
item.querySelector('input[name*=\'_destroy\']').value = 1
|
208
208
|
item.classList.add('d-none')
|
209
209
|
|
210
210
|
// Reset positions
|
@@ -219,19 +219,19 @@ export default class extends Controller {
|
|
219
219
|
|
220
220
|
itemByBlobId (blobId) {
|
221
221
|
return this.itemTargets.find((item) => {
|
222
|
-
return item.querySelector(
|
222
|
+
return item.querySelector('input[name*=\'blob_id\']').value === blobId
|
223
223
|
})
|
224
224
|
}
|
225
225
|
|
226
226
|
activeItems () {
|
227
227
|
return this.itemTargets.filter((item) => {
|
228
|
-
return item.querySelector(
|
228
|
+
return item.querySelector('input[name$=\'[_destroy]\']').value === 'false'
|
229
229
|
})
|
230
230
|
}
|
231
231
|
|
232
232
|
activeIds () {
|
233
233
|
return this.activeItems().map((item) => {
|
234
|
-
return item.querySelector(
|
234
|
+
return item.querySelector('input[name$=\'[blob_id]\']').value
|
235
235
|
})
|
236
236
|
}
|
237
|
-
}
|
237
|
+
}
|
@@ -27,11 +27,11 @@ export default class extends Controller {
|
|
27
27
|
}
|
28
28
|
|
29
29
|
// Methods
|
30
|
-
hidePlaceholder() {
|
30
|
+
hidePlaceholder () {
|
31
31
|
this.placeholderTarget.classList.add('d-none')
|
32
32
|
}
|
33
33
|
|
34
|
-
handleInputChange() {
|
34
|
+
handleInputChange () {
|
35
35
|
this.validate()
|
36
36
|
}
|
37
37
|
|
@@ -104,7 +104,7 @@ export default class extends Controller {
|
|
104
104
|
return count >= this.minSelectedItems() && count <= this.maxSelectedItems()
|
105
105
|
}
|
106
106
|
|
107
|
-
updateCount() {
|
108
|
-
this.countTarget.innerHTML = this.idCheckboxTargets.filter(checkbox => checkbox.checked).length
|
107
|
+
updateCount () {
|
108
|
+
this.countTarget.innerHTML = this.idCheckboxTargets.filter(checkbox => checkbox.checked).length
|
109
109
|
}
|
110
|
-
}
|
110
|
+
}
|
@@ -26,9 +26,9 @@ export default class extends Controller {
|
|
26
26
|
}
|
27
27
|
|
28
28
|
updateCountLength () {
|
29
|
-
const
|
30
|
-
const
|
29
|
+
const currentLength = this.textareaTarget.value.length
|
30
|
+
const maximumLength = this.textareaTarget.getAttribute('maxlength')
|
31
31
|
|
32
|
-
this.countTarget.textContent = `${
|
32
|
+
this.countTarget.textContent = `${currentLength}/${maximumLength}`
|
33
33
|
}
|
34
34
|
}
|
@@ -10211,7 +10211,7 @@ var media_controller_default = class extends Controller {
|
|
10211
10211
|
this.placeholderTarget.classList.add("d-none");
|
10212
10212
|
}
|
10213
10213
|
enableItem(item) {
|
10214
|
-
item.querySelector(
|
10214
|
+
item.querySelector("input[name*='_destroy']").value = false;
|
10215
10215
|
item.classList.remove("d-none");
|
10216
10216
|
}
|
10217
10217
|
createItem(item) {
|
@@ -10219,9 +10219,9 @@ var media_controller_default = class extends Controller {
|
|
10219
10219
|
const html = this.randomizeIds(template);
|
10220
10220
|
this.thumbnailsTarget.insertAdjacentHTML("beforeend", html);
|
10221
10221
|
const newItem = this.itemTargets.pop();
|
10222
|
-
newItem.querySelector(
|
10223
|
-
newItem.querySelector(
|
10224
|
-
const editButton = newItem.querySelector(
|
10222
|
+
newItem.querySelector('input[name*="[blob_id]"]').value = item.blobId;
|
10223
|
+
newItem.querySelector('input[name*="[_destroy]"]').value = false;
|
10224
|
+
const editButton = newItem.querySelector('[data-media-target="editButton"]');
|
10225
10225
|
editButton.setAttribute("href", editButton.getAttribute("href").replace("$1", item.blobId));
|
10226
10226
|
const oldThumbnail = newItem.querySelector(".h-thumbnail");
|
10227
10227
|
const newThumbnail = item.thumbnail.cloneNode(true);
|
@@ -10241,7 +10241,7 @@ var media_controller_default = class extends Controller {
|
|
10241
10241
|
});
|
10242
10242
|
}
|
10243
10243
|
removeItem(item) {
|
10244
|
-
item.querySelector(
|
10244
|
+
item.querySelector("input[name*='_destroy']").value = 1;
|
10245
10245
|
item.classList.add("d-none");
|
10246
10246
|
this.resetPositions();
|
10247
10247
|
this.syncIds();
|
@@ -10249,17 +10249,17 @@ var media_controller_default = class extends Controller {
|
|
10249
10249
|
}
|
10250
10250
|
itemByBlobId(blobId) {
|
10251
10251
|
return this.itemTargets.find((item) => {
|
10252
|
-
return item.querySelector(
|
10252
|
+
return item.querySelector("input[name*='blob_id']").value === blobId;
|
10253
10253
|
});
|
10254
10254
|
}
|
10255
10255
|
activeItems() {
|
10256
10256
|
return this.itemTargets.filter((item) => {
|
10257
|
-
return item.querySelector(
|
10257
|
+
return item.querySelector("input[name$='[_destroy]']").value === "false";
|
10258
10258
|
});
|
10259
10259
|
}
|
10260
10260
|
activeIds() {
|
10261
10261
|
return this.activeItems().map((item) => {
|
10262
|
-
return item.querySelector(
|
10262
|
+
return item.querySelector("input[name$='[blob_id]']").value;
|
10263
10263
|
});
|
10264
10264
|
}
|
10265
10265
|
};
|
@@ -15864,9 +15864,9 @@ var textarea_controller_default = class extends Controller {
|
|
15864
15864
|
}
|
15865
15865
|
}
|
15866
15866
|
updateCountLength() {
|
15867
|
-
const
|
15868
|
-
const
|
15869
|
-
this.countTarget.textContent = `${
|
15867
|
+
const currentLength = this.textareaTarget.value.length;
|
15868
|
+
const maximumLength = this.textareaTarget.getAttribute("maxlength");
|
15869
|
+
this.countTarget.textContent = `${currentLength}/${maximumLength}`;
|
15870
15870
|
}
|
15871
15871
|
};
|
15872
15872
|
|
@@ -10180,7 +10180,6 @@ fieldset:disabled .btn {
|
|
10180
10180
|
.ts-wrapper.form-select {
|
10181
10181
|
padding: 0;
|
10182
10182
|
height: auto;
|
10183
|
-
box-shadow: none;
|
10184
10183
|
}
|
10185
10184
|
.ts-wrapper.form-select .ts-control,
|
10186
10185
|
.ts-wrapper.form-select.single.input-active .ts-control {
|
@@ -12944,7 +12943,10 @@ span.flatpickr-weekday {
|
|
12944
12943
|
padding: 0.375rem 0.75rem !important;
|
12945
12944
|
position: relative;
|
12946
12945
|
}
|
12947
|
-
.rx-content p
|
12946
|
+
.rx-content p,
|
12947
|
+
.rx-content ul li,
|
12948
|
+
.rx-content ol li,
|
12949
|
+
.rx-content td {
|
12948
12950
|
font-size: 0.9rem;
|
12949
12951
|
line-height: 1.5;
|
12950
12952
|
color: #212529;
|
@@ -12971,9 +12973,6 @@ span.flatpickr-weekday {
|
|
12971
12973
|
content: " *";
|
12972
12974
|
color: #dc3545;
|
12973
12975
|
}
|
12974
|
-
.card-body .mb-3:last-child {
|
12975
|
-
margin-bottom: 0 !important;
|
12976
|
-
}
|
12977
12976
|
.h-autocomplete {
|
12978
12977
|
position: absolute;
|
12979
12978
|
top: calc(100% + 2px);
|
@@ -13100,9 +13099,6 @@ span.flatpickr-weekday {
|
|
13100
13099
|
.repeater-row:hover .repeater-row-handle {
|
13101
13100
|
visibility: visible;
|
13102
13101
|
}
|
13103
|
-
.repeater-row .mb-3:last-of-type {
|
13104
|
-
margin-bottom: 0 !important;
|
13105
|
-
}
|
13106
13102
|
.repeater-row-remove {
|
13107
13103
|
position: absolute;
|
13108
13104
|
top: calc(50% - 17px);
|
@@ -2,7 +2,11 @@ module Headmin
|
|
2
2
|
module Pagination
|
3
3
|
def paginate(collection)
|
4
4
|
@records_filtered = collection.count
|
5
|
-
collection.
|
5
|
+
if collection.is_a?(Array)
|
6
|
+
Kaminari.paginate_array(collection).page(page).per(per_page)
|
7
|
+
else
|
8
|
+
collection.page(page).per(per_page)
|
9
|
+
end
|
6
10
|
end
|
7
11
|
|
8
12
|
def page
|
@@ -13,7 +13,7 @@ module Headmin
|
|
13
13
|
accepts_nested_attributes_for :fields, allow_destroy: true
|
14
14
|
|
15
15
|
# field_type: :files, :file
|
16
|
-
has_many_attached :files
|
16
|
+
has_many_attached :files, dependent: :detach
|
17
17
|
accepts_nested_attributes_for :files_attachments, allow_destroy: true
|
18
18
|
end
|
19
19
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Headmin
|
2
|
+
module Filter
|
3
|
+
class Association < Headmin::Filter::Base
|
4
|
+
OPERATORS = %w[in not_in]
|
5
|
+
|
6
|
+
def cast_value(value)
|
7
|
+
is_i?(value) ? value.to_i : 0
|
8
|
+
end
|
9
|
+
|
10
|
+
def query(collection)
|
11
|
+
return collection unless @instructions.any?
|
12
|
+
|
13
|
+
# Store the collections' class for later use
|
14
|
+
@parent_class = collection.is_a?(Class) ? collection : collection.klass
|
15
|
+
|
16
|
+
# Join table if necessary
|
17
|
+
collection = collection.joins(attribute) if has_many?
|
18
|
+
|
19
|
+
# Build query and execute
|
20
|
+
query = nil
|
21
|
+
@instructions.each do |instruction|
|
22
|
+
query = build_query(query, collection, instruction)
|
23
|
+
end
|
24
|
+
collection.where(query)
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_query(query, collection, instruction)
|
28
|
+
query_operator = convert_to_query_operator(instruction[:operator])
|
29
|
+
query_value = convert_to_query_value(instruction[:value], instruction[:operator])
|
30
|
+
query_operator, query_value = process_null_operators(query_operator, query_value)
|
31
|
+
|
32
|
+
new_query = association_column.send(query_operator, query_value)
|
33
|
+
|
34
|
+
query ? query.send(instruction[:conditional], new_query) : new_query
|
35
|
+
end
|
36
|
+
|
37
|
+
def display_value(value)
|
38
|
+
if value.is_a? Array
|
39
|
+
value.first.to_s
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def reflection
|
46
|
+
@parent_class.reflect_on_association(attribute)
|
47
|
+
end
|
48
|
+
|
49
|
+
def macro
|
50
|
+
reflection.macro
|
51
|
+
end
|
52
|
+
|
53
|
+
def association_class
|
54
|
+
reflection.klass
|
55
|
+
end
|
56
|
+
|
57
|
+
def foreign_key
|
58
|
+
reflection.foreign_key
|
59
|
+
end
|
60
|
+
|
61
|
+
def association_column
|
62
|
+
if has_many?
|
63
|
+
association_class.arel_table[:id]
|
64
|
+
else
|
65
|
+
@parent_class.arel_table[foreign_key]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def has_many?
|
70
|
+
macro == :has_many
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def is_i?(value)
|
76
|
+
# Regex: this selects signed digits (\d) only, it is then checked to the value, e.g.:
|
77
|
+
# is_i?("3") = true
|
78
|
+
# is_i?("-3") = true
|
79
|
+
# is_i?("3a") = false
|
80
|
+
# is_i?("3.2") = false
|
81
|
+
|
82
|
+
/\A[-+]?\d+\z/.match(value)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Headmin
|
2
|
+
module Filter
|
3
|
+
class AssociationView < FilterView
|
4
|
+
def base_options
|
5
|
+
keys = %i[name label form]
|
6
|
+
options = to_h.slice(*keys)
|
7
|
+
default_base_options.merge(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def input_options
|
11
|
+
keys = %i[form]
|
12
|
+
options = to_h.slice(*keys)
|
13
|
+
default_input_options.merge(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def collection
|
17
|
+
@collection || association_model.all.map { |record| [record.to_s, record.id] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def association_model
|
21
|
+
reflection.klass
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def id
|
27
|
+
"#{name}_value"
|
28
|
+
end
|
29
|
+
|
30
|
+
def name
|
31
|
+
@name || attribute
|
32
|
+
end
|
33
|
+
|
34
|
+
def label
|
35
|
+
@label || I18n.t("attributes.#{attribute}", default: association_model.model_name.human(count: collection? ? 2 : 1))
|
36
|
+
end
|
37
|
+
|
38
|
+
def reflection
|
39
|
+
form.object.class.reflect_on_association(attribute)
|
40
|
+
end
|
41
|
+
|
42
|
+
def collection?
|
43
|
+
reflection.collection?
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_base_options
|
47
|
+
{
|
48
|
+
label: label,
|
49
|
+
name: attribute,
|
50
|
+
display_values: collection,
|
51
|
+
filter: Headmin::Filter::Association.new(name, @params),
|
52
|
+
allowed_operators: Headmin::Filter::Association::OPERATORS
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_input_options
|
57
|
+
{
|
58
|
+
label: false,
|
59
|
+
wrapper: false,
|
60
|
+
name: nil,
|
61
|
+
id: id,
|
62
|
+
data: {
|
63
|
+
action: "change->filter#updateHiddenValue",
|
64
|
+
filter_target: "value",
|
65
|
+
filter_row_target: "original"
|
66
|
+
},
|
67
|
+
collection: collection,
|
68
|
+
selected: selected,
|
69
|
+
class: "form-select"
|
70
|
+
}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -6,7 +6,7 @@ module Headmin
|
|
6
6
|
|
7
7
|
OPERATORS_CONVERT_TO = {
|
8
8
|
convert_to_range: %w[between not_between],
|
9
|
-
convert_to_array: %w[in not_in],
|
9
|
+
convert_to_array: %w[in not_in in_all in_any],
|
10
10
|
convert_to_value: %w[eq not_eq gt gteq lt lteq matches does_not_match is_null is_not_null starts_with ends_with]
|
11
11
|
}
|
12
12
|
|
@@ -21,12 +21,13 @@ module Headmin
|
|
21
21
|
}
|
22
22
|
|
23
23
|
# Methods
|
24
|
-
def initialize(attribute, params)
|
25
|
-
@
|
26
|
-
@
|
24
|
+
def initialize(attribute, params, association: nil)
|
25
|
+
@attribute = association ? "#{association}_#{attribute}".to_sym : attribute
|
26
|
+
@raw_value = params[@attribute]
|
27
|
+
@association = association
|
27
28
|
@instructions = []
|
28
29
|
|
29
|
-
if params.key?(attribute)
|
30
|
+
if params.key?(@attribute)
|
30
31
|
parse(@raw_value)
|
31
32
|
end
|
32
33
|
end
|
@@ -61,7 +62,8 @@ module Headmin
|
|
61
62
|
query = build_query(query, collection, instruction)
|
62
63
|
end
|
63
64
|
|
64
|
-
collection.
|
65
|
+
collection = collection.joins(@association) if @association
|
66
|
+
collection.distinct.where(query)
|
65
67
|
end
|
66
68
|
|
67
69
|
def cast_value(value)
|
@@ -74,6 +76,30 @@ module Headmin
|
|
74
76
|
value
|
75
77
|
end
|
76
78
|
|
79
|
+
def build_query(query, collection, instruction)
|
80
|
+
query_operator = convert_to_query_operator(instruction[:operator])
|
81
|
+
query_value = convert_to_query_value(instruction[:value], instruction[:operator])
|
82
|
+
|
83
|
+
query_operator, query_value = process_null_operators(query_operator, query_value)
|
84
|
+
|
85
|
+
model = collection.is_a?(Class) ? collection : collection.model
|
86
|
+
|
87
|
+
if @association
|
88
|
+
# Association attributes are passed through as {association}_{attribute}, so we need to transform this into {attribute}
|
89
|
+
new_attribute = attribute.to_s.gsub("#{@association}_", "").to_sym
|
90
|
+
new_model = model.reflect_on_association(@association)
|
91
|
+
|
92
|
+
# In case the association cannot be found, raise a well defined error
|
93
|
+
raise UnknownAssociation if new_model.nil?
|
94
|
+
|
95
|
+
new_query = new_model.klass.arel_table[new_attribute].send(query_operator, query_value)
|
96
|
+
else
|
97
|
+
new_query = model.arel_table[attribute].send(query_operator, query_value)
|
98
|
+
end
|
99
|
+
|
100
|
+
query ? query.send(instruction[:conditional], new_query) : new_query
|
101
|
+
end
|
102
|
+
|
77
103
|
private
|
78
104
|
|
79
105
|
def parse(string)
|
@@ -139,18 +165,6 @@ module Headmin
|
|
139
165
|
process_value(string, operator)
|
140
166
|
end
|
141
167
|
|
142
|
-
def build_query(query, collection, instruction)
|
143
|
-
query_operator = convert_to_query_operator(instruction[:operator])
|
144
|
-
query_value = convert_to_query_value(instruction[:value], instruction[:operator])
|
145
|
-
|
146
|
-
query_operator, query_value = process_null_operators(query_operator, query_value)
|
147
|
-
|
148
|
-
model = collection.is_a?(Class) ? collection : collection.model
|
149
|
-
new_query = model.arel_table[attribute].send(query_operator, query_value)
|
150
|
-
|
151
|
-
query ? query.send(instruction[:conditional], new_query) : new_query
|
152
|
-
end
|
153
|
-
|
154
168
|
def process_null_operators(operator, value)
|
155
169
|
# In case of null operators (is_null and is_not_null), we have to intercept the operator and value values
|
156
170
|
# and transform them to the correct operator (eq or not_eq) and value (nil)
|
@@ -215,7 +229,10 @@ module Headmin
|
|
215
229
|
def build_instruction_string(instruction, display_values)
|
216
230
|
conditional = instruction[:conditional].present? ? "#{I18n.t("headmin.filters.conditionals.#{instruction[:conditional]}")} " : nil
|
217
231
|
operator = I18n.t("headmin.filters.operators.#{instruction[:operator]}")
|
218
|
-
|
232
|
+
|
233
|
+
display_value = instruction[:value].is_a?(Array) ? instruction[:value].first.to_s : instruction[:value]
|
234
|
+
|
235
|
+
value = display_values[:display_values].find { |item| item.second.to_s == display_value }
|
219
236
|
value = value.present? ? value.first : instruction[:value]
|
220
237
|
|
221
238
|
value = if instruction[:operator] == "is_null" || instruction[:operator] == "is_not_null"
|
@@ -234,5 +251,8 @@ module Headmin
|
|
234
251
|
|
235
252
|
class NotImplementedMethodError < StandardError
|
236
253
|
end
|
254
|
+
|
255
|
+
class UnknownAssociation < StandardError
|
256
|
+
end
|
237
257
|
end
|
238
258
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Headmin
|
2
2
|
module Filter
|
3
|
-
class BooleanView <
|
3
|
+
class BooleanView < FilterView
|
4
4
|
def base_options
|
5
5
|
keys = %i[name label form]
|
6
6
|
options = to_h.slice(*keys)
|
@@ -28,10 +28,6 @@ module Headmin
|
|
28
28
|
@name || attribute
|
29
29
|
end
|
30
30
|
|
31
|
-
def label
|
32
|
-
@label || I18n.t("attributes.#{attribute}", default: name.to_s)
|
33
|
-
end
|
34
|
-
|
35
31
|
def default_base_options
|
36
32
|
{
|
37
33
|
label: label,
|
@@ -47,6 +43,7 @@ module Headmin
|
|
47
43
|
label: false,
|
48
44
|
wrapper: false,
|
49
45
|
id: id,
|
46
|
+
name: nil,
|
50
47
|
collection: [[I18n.t("headmin.filters.values.true"), 1], [I18n.t("headmin.filters.values.false"), 0]],
|
51
48
|
selected: value ? 1 : 0,
|
52
49
|
data: {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Headmin
|
2
2
|
module Filter
|
3
|
-
class DateView <
|
3
|
+
class DateView < FilterView
|
4
4
|
def base_options
|
5
5
|
keys = %i[name label form]
|
6
6
|
options = to_h.slice(*keys)
|
@@ -23,10 +23,6 @@ module Headmin
|
|
23
23
|
@name || attribute
|
24
24
|
end
|
25
25
|
|
26
|
-
def label
|
27
|
-
@label || I18n.t("attributes.#{attribute}", default: name.to_s)
|
28
|
-
end
|
29
|
-
|
30
26
|
def default_base_options
|
31
27
|
{
|
32
28
|
label: label,
|
@@ -41,6 +37,7 @@ module Headmin
|
|
41
37
|
label: false,
|
42
38
|
wrapper: false,
|
43
39
|
id: id,
|
40
|
+
name: nil,
|
44
41
|
data: {action: "change->filter#updateHiddenValue",
|
45
42
|
filter_target: "value",
|
46
43
|
filter_row_target: "original"},
|