madmin 2.6.0 → 3.0.0
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/README.md +107 -4
- data/app/assets/stylesheets/madmin/actiontext.css +31 -0
- data/app/assets/stylesheets/madmin/base.css +22 -11
- data/app/assets/stylesheets/madmin/buttons.css +9 -9
- data/app/assets/stylesheets/madmin/forms.css +263 -7
- data/app/assets/stylesheets/madmin/pagination.css +10 -23
- data/app/assets/stylesheets/madmin/reset.css +1 -1
- data/app/assets/stylesheets/madmin/sidebar.css +6 -6
- data/app/assets/stylesheets/madmin/tables.css +1 -1
- data/app/controllers/madmin/base_controller.rb +1 -5
- data/app/controllers/madmin/resource_controller.rb +38 -17
- data/app/helpers/madmin/application_helper.rb +15 -2
- data/app/helpers/madmin/filters_helper.rb +26 -0
- data/app/helpers/madmin/sort_helper.rb +1 -1
- data/app/javascript/madmin/controllers/filters_controller.js +35 -0
- data/app/javascript/madmin/controllers/nested_form_controller.js +5 -0
- data/app/javascript/madmin/controllers/select_controller.js +269 -22
- data/app/views/layouts/madmin/application.html.erb +1 -0
- data/app/views/madmin/application/_active_filters.html.erb +15 -0
- data/app/views/madmin/application/_filter.html.erb +13 -0
- data/app/views/madmin/application/_filters.html.erb +32 -0
- data/app/views/madmin/application/_javascript.html.erb +0 -2
- data/app/views/madmin/application/_pagination.html.erb +32 -0
- data/app/views/madmin/application/edit.html.erb +1 -1
- data/app/views/madmin/application/index.html.erb +13 -12
- data/app/views/madmin/application/new.html.erb +1 -1
- data/app/views/madmin/application/show.html.erb +1 -1
- data/app/views/madmin/fields/array/_form.html.erb +1 -0
- data/app/views/madmin/fields/array/_index.html.erb +1 -0
- data/app/views/madmin/fields/array/_show.html.erb +1 -0
- data/app/views/madmin/fields/attachment/_form.html.erb +0 -4
- data/app/views/madmin/fields/attachment/_show.html.erb +3 -1
- data/app/views/madmin/fields/attachments/_index.html.erb +1 -1
- data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -1
- data/app/views/madmin/fields/belongs_to/_index.html.erb +5 -2
- data/app/views/madmin/fields/belongs_to/_show.html.erb +1 -1
- data/app/views/madmin/fields/enum/_form.html.erb +1 -1
- data/app/views/madmin/fields/has_many/_form.html.erb +1 -1
- data/app/views/madmin/fields/has_many/_index.html.erb +1 -1
- data/app/views/madmin/fields/has_many/_show.html.erb +3 -11
- data/app/views/madmin/fields/has_one/_index.html.erb +5 -1
- data/app/views/madmin/fields/has_one/_show.html.erb +1 -1
- data/app/views/madmin/fields/json/_form.html.erb +1 -1
- data/app/views/madmin/fields/json/_show.html.erb +1 -1
- data/app/views/madmin/fields/nested_has_many/_index.html.erb +1 -1
- data/app/views/madmin/fields/nested_has_many/_show.html.erb +3 -11
- data/app/views/madmin/fields/nested_has_one/_form.html.erb +18 -0
- data/app/views/madmin/fields/polymorphic/_form.html.erb +1 -2
- data/app/views/madmin/fields/polymorphic/_index.html.erb +5 -1
- data/app/views/madmin/fields/polymorphic/_show.html.erb +1 -1
- data/app/views/madmin/fields/select/_form.html.erb +1 -1
- data/config/importmap.rb +0 -1
- data/config/locales/el.yml +44 -0
- data/config/locales/en.yml +44 -0
- data/config/locales/fr.yml +44 -0
- data/config/locales/ko.yml +44 -0
- data/lib/madmin/field.rb +27 -1
- data/lib/madmin/fields/array.rb +16 -0
- data/lib/madmin/fields/attachment.rb +7 -0
- data/lib/madmin/fields/belongs_to.rb +2 -1
- data/lib/madmin/fields/boolean.rb +1 -0
- data/lib/madmin/fields/currency.rb +7 -0
- data/lib/madmin/fields/date.rb +1 -0
- data/lib/madmin/fields/date_time.rb +1 -0
- data/lib/madmin/fields/decimal.rb +1 -0
- data/lib/madmin/fields/float.rb +1 -0
- data/lib/madmin/fields/has_many.rb +6 -23
- data/lib/madmin/fields/integer.rb +1 -0
- data/lib/madmin/fields/json.rb +21 -0
- data/lib/madmin/fields/nested_has_many.rb +2 -2
- data/lib/madmin/fields/nested_has_one.rb +48 -0
- data/lib/madmin/fields/polymorphic.rb +5 -0
- data/lib/madmin/fields/string.rb +2 -0
- data/lib/madmin/fields/text.rb +2 -0
- data/lib/madmin/filter.rb +96 -0
- data/lib/madmin/menu.rb +2 -1
- data/lib/madmin/page.rb +61 -0
- data/lib/madmin/pagination.rb +50 -0
- data/lib/madmin/resource.rb +85 -7
- data/lib/madmin/search.rb +5 -7
- data/lib/madmin/version.rb +1 -1
- data/lib/madmin.rb +45 -2
- metadata +17 -16
|
@@ -4,5 +4,3 @@
|
|
|
4
4
|
<%= tag.script 'import "lexxy"'.html_safe, type: "module" if defined?(::Lexxy) %>
|
|
5
5
|
|
|
6
6
|
<%= stylesheet_link_tag *Madmin.stylesheets, "data-turbo-track": "reload" %>
|
|
7
|
-
<%= stylesheet_link_tag "https://unpkg.com/flatpickr/dist/flatpickr.min.css", "data-turbo-track": "reload" %>
|
|
8
|
-
<%= stylesheet_link_tag "https://unpkg.com/tom-select/dist/css/tom-select.min.css", "data-turbo-track": "reload" %>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<div class="pagination">
|
|
2
|
+
<% if page.last > 1 %>
|
|
3
|
+
<% page_url = ->(number) { "#{request.path}?#{request.query_parameters.merge(page.param => number).to_query}" } %>
|
|
4
|
+
<nav class="pages" aria-label="<%= t("madmin.pagination.label") %>">
|
|
5
|
+
<%= link_to_if page.prev, "<", page.prev && page_url.call(page.prev), aria: { label: t("madmin.pagination.previous") } do %>
|
|
6
|
+
<a role="link" aria-disabled="true" aria-label="<%= t("madmin.pagination.previous") %>"><</a>
|
|
7
|
+
<% end %>
|
|
8
|
+
|
|
9
|
+
<% page.series.each do |number| %>
|
|
10
|
+
<% if number == :gap %>
|
|
11
|
+
<a role="separator" aria-disabled="true">…</a>
|
|
12
|
+
<% elsif number == page.page %>
|
|
13
|
+
<a role="link" aria-disabled="true" aria-current="page"><%= number %></a>
|
|
14
|
+
<% else %>
|
|
15
|
+
<%= link_to number, page_url.call(number) %>
|
|
16
|
+
<% end %>
|
|
17
|
+
<% end %>
|
|
18
|
+
|
|
19
|
+
<%= link_to_if page.next, ">", page.next && page_url.call(page.next), aria: { label: t("madmin.pagination.next") } do %>
|
|
20
|
+
<a role="link" aria-disabled="true" aria-label="<%= t("madmin.pagination.next") %>">></a>
|
|
21
|
+
<% end %>
|
|
22
|
+
</nav>
|
|
23
|
+
<% end %>
|
|
24
|
+
|
|
25
|
+
<span class="pagination-info">
|
|
26
|
+
<% if page.to.zero? %>
|
|
27
|
+
<%= t("madmin.pagination.empty") %>
|
|
28
|
+
<% else %>
|
|
29
|
+
<%= t("madmin.pagination.info", from: page.from, to: page.to, count: page.count) %>
|
|
30
|
+
<% end %>
|
|
31
|
+
</span>
|
|
32
|
+
</div>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<header class="header">
|
|
4
4
|
<h1>
|
|
5
|
-
<%= link_to resource.
|
|
5
|
+
<%= link_to resource.friendly_plural_name, resource.index_path %>
|
|
6
6
|
/
|
|
7
7
|
<strong><%= t("madmin.actions.edit") %> <%= link_to resource.display_name(@record), resource.show_path(@record) %></strong>
|
|
8
8
|
</h1>
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
<%= content_for :title, resource.
|
|
1
|
+
<%= content_for :title, resource.friendly_plural_name %>
|
|
2
2
|
|
|
3
3
|
<header class="header">
|
|
4
|
-
<h1><%= resource.
|
|
4
|
+
<h1><%= resource.friendly_plural_name %></h1>
|
|
5
5
|
|
|
6
6
|
<div class="actions">
|
|
7
7
|
<form class="search">
|
|
8
8
|
<%= hidden_field_tag :page, params[:page], value: 1 %>
|
|
9
9
|
<%= search_field_tag :q, params[:q], placeholder: t("madmin.search.placeholder") %>
|
|
10
|
+
<% filters.map(&:to_h).each do |filter| %>
|
|
11
|
+
<% filter.each do |name, value| %>
|
|
12
|
+
<%= hidden_field_tag "filters[][#{name}]", value, id: nil %>
|
|
13
|
+
<% end %>
|
|
14
|
+
<% end %>
|
|
10
15
|
</form>
|
|
11
16
|
|
|
12
17
|
<%= link_to clear_search_params, class: "btn btn-secondary" do %>
|
|
@@ -15,6 +20,8 @@
|
|
|
15
20
|
</svg>
|
|
16
21
|
<% end if params[:q].present? %>
|
|
17
22
|
|
|
23
|
+
<%= render "filters" %>
|
|
24
|
+
|
|
18
25
|
<% resource.collection_actions.each do |action| %>
|
|
19
26
|
<%= instance_exec(&action) %>
|
|
20
27
|
<% end %>
|
|
@@ -27,13 +34,15 @@
|
|
|
27
34
|
</div>
|
|
28
35
|
</header>
|
|
29
36
|
|
|
37
|
+
<%= render "active_filters" %>
|
|
38
|
+
|
|
30
39
|
<nav class="scopes">
|
|
31
40
|
<% if resource.scopes.any? %>
|
|
32
41
|
<%= link_to t("madmin.actions.all"), resource.index_path, class: class_names("btn btn-secondary", {"active" => params[:scope].blank?}) %>
|
|
33
42
|
<% end %>
|
|
34
43
|
|
|
35
44
|
<% resource.scopes.each do |scope| %>
|
|
36
|
-
<%= link_to scope
|
|
45
|
+
<%= link_to resource.scope_label(scope), index_path_with(scope: scope), class: class_names("btn btn-secondary", {"active" => params[:scope] == scope.to_s}) %>
|
|
37
46
|
<% end %>
|
|
38
47
|
</nav>
|
|
39
48
|
|
|
@@ -74,12 +83,4 @@
|
|
|
74
83
|
</table>
|
|
75
84
|
</div>
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
<% if @pagy.respond_to?(:series_nav) %>
|
|
79
|
-
<%== @pagy.series_nav if @pagy.last > 1 %>
|
|
80
|
-
<%== @pagy.info_tag %>
|
|
81
|
-
<% else %>
|
|
82
|
-
<%== pagy_nav @pagy if @pagy.last > 1 %>
|
|
83
|
-
<%== pagy_info @pagy %>
|
|
84
|
-
<% end %>
|
|
85
|
-
</div>
|
|
86
|
+
<%= render "pagination", page: @page %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= form.text_field field.attribute_name, value: field.text(record), class: "form-input" %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= field.text(record) %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= field.text(record) %>
|
|
@@ -3,9 +3,5 @@
|
|
|
3
3
|
<% if form.object.persisted? %>
|
|
4
4
|
<div class="mt-2">
|
|
5
5
|
<%= render partial: field.to_partial_path("show"), locals: {field: field, record: record} %>
|
|
6
|
-
|
|
7
|
-
<% if (value = field.value(record) && value&.attached?) %>
|
|
8
|
-
<%= link_to t("madmin.actions.remove"), Madmin.resource_for(value).show_path(value), data: { turbo_method: :delete, turbo_confirm: t("madmin.confirmations.remove_with_changes")} %>
|
|
9
|
-
<% end %>
|
|
10
6
|
</div>
|
|
11
7
|
<% end %>
|
|
@@ -5,5 +5,7 @@
|
|
|
5
5
|
<%= link_to attachment.filename, main_app.url_for(attachment), target: :_blank, class: "text-blue-500 underline" %>
|
|
6
6
|
<% end %>
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
<% if (remove_path = field.remove_path(attachment)) %>
|
|
9
|
+
<%= link_to t("madmin.actions.remove"), remove_path, data: { turbo_method: :delete, turbo_confirm: t("madmin.confirmations.remove_with_changes")} %>
|
|
10
|
+
<% end %>
|
|
9
11
|
<% end %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= t("madmin.fields.attachment", count: field.value(record).
|
|
1
|
+
<%= t("madmin.fields.attachment", count: field.value(record).size) %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= form.select field.to_param, field.options_for_select(record), { prompt: true }, { data: { controller: "select", select_url_value: field.index_path } } %>
|
|
1
|
+
<%= form.select field.to_param, field.options_for_select(record), { prompt: true }, { class: "form-input", data: { controller: "select", select_url_value: field.index_path } } %>
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
<% if (object = field.value(record)) %>
|
|
2
|
-
<%
|
|
3
|
-
|
|
2
|
+
<% if (associated_resource = field.associated_resource_for(object)) %>
|
|
3
|
+
<%= link_to_record associated_resource, object %>
|
|
4
|
+
<% else %>
|
|
5
|
+
<%= render partial: "missing_resource", locals: { resource_name: Madmin.resource_name_for(object) } %>
|
|
6
|
+
<% end %>
|
|
4
7
|
<% end %>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% if (object = field.value(record)) %>
|
|
2
2
|
<% if (associated_resource = field.associated_resource_for(object)) %>
|
|
3
|
-
<%=
|
|
3
|
+
<%= link_to_record associated_resource, object %>
|
|
4
4
|
<% else %>
|
|
5
5
|
<%= render partial: "missing_resource", locals: { resource_name: Madmin.resource_name_for(object) } %>
|
|
6
6
|
<% end %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, {
|
|
1
|
+
<%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, { class: "form-input" } %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= form.select "#{field.attribute_name.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, data: { controller: "select", select_url_value: field.index_path } } %>
|
|
1
|
+
<%= form.select "#{field.attribute_name.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, class: "form-input", data: { controller: "select", select_url_value: field.index_path } } %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= pluralize field.value(record).
|
|
1
|
+
<%= pluralize field.value(record).size, field.attribute_name.to_s %>
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
<%
|
|
1
|
+
<% page, records = field.paginated_value(record, params) %>
|
|
2
2
|
<% records.each do |object| %>
|
|
3
3
|
<% if (associated_resource = field.associated_resource_for(object)) %>
|
|
4
|
-
<div><%=
|
|
4
|
+
<div><%= link_to_record associated_resource, object %></div>
|
|
5
5
|
<% else %>
|
|
6
6
|
<%= render partial: "missing_resource", locals: { resource_name: Madmin.resource_name_for(object) } %>
|
|
7
7
|
<% end %>
|
|
8
8
|
<% end %>
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
<% if pagy.respond_to?(:series_nav) %>
|
|
12
|
-
<%== pagy.series_nav if pagy.last > 1 %>
|
|
13
|
-
<%== pagy.info_tag %>
|
|
14
|
-
<% else %>
|
|
15
|
-
<%== pagy_nav pagy if pagy.last > 1 %>
|
|
16
|
-
<%== pagy_info pagy %>
|
|
17
|
-
<% end %>
|
|
18
|
-
</div>
|
|
10
|
+
<%= render "pagination", page: page %>
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
<% if (object = field.value(record)) %>
|
|
2
|
-
|
|
2
|
+
<% if (associated_resource = field.associated_resource_for(object)) %>
|
|
3
|
+
<%= link_to_record associated_resource, object %>
|
|
4
|
+
<% else %>
|
|
5
|
+
<%= render partial: "missing_resource", locals: { resource_name: Madmin.resource_name_for(object) } %>
|
|
6
|
+
<% end %>
|
|
3
7
|
<% end %>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% if (object = field.value(record)) %>
|
|
2
2
|
<% if (associated_resource = field.associated_resource_for(object)) %>
|
|
3
|
-
<%=
|
|
3
|
+
<%= link_to_record associated_resource, object %>
|
|
4
4
|
<% else %>
|
|
5
5
|
<%= render partial: "missing_resource", locals: { resource_name: Madmin.resource_name_for(object) } %>
|
|
6
6
|
<% end %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= form.text_area field.attribute_name, class: "form-input" %>
|
|
1
|
+
<%= form.text_area field.attribute_name, value: field.json(record), class: "form-input" %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
<pre><%= field.json(record) %></pre>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= pluralize field.value(record).
|
|
1
|
+
<%= pluralize field.value(record).size, field.attribute_name.to_s %>
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
<%
|
|
1
|
+
<% page, records = field.paginated_value(record, params) %>
|
|
2
2
|
<% records.each do |object| %>
|
|
3
3
|
<% if (associated_resource = field.associated_resource_for(object)) %>
|
|
4
|
-
<div><%=
|
|
4
|
+
<div><%= link_to_record associated_resource, object %></div>
|
|
5
5
|
<% else %>
|
|
6
6
|
<%= render partial: "missing_resource", locals: { resource_name: Madmin.resource_name_for(object) } %>
|
|
7
7
|
<% end %>
|
|
8
8
|
<% end %>
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
<% if pagy.respond_to?(:series_nav) %>
|
|
12
|
-
<%== pagy.series_nav if pagy.last > 1 %>
|
|
13
|
-
<%== pagy.info_tag %>
|
|
14
|
-
<% else %>
|
|
15
|
-
<%== pagy_nav pagy if pagy.last > 1 %>
|
|
16
|
-
<%== pagy_info pagy %>
|
|
17
|
-
<% end %>
|
|
18
|
-
</div>
|
|
10
|
+
<%= render "pagination", page: page %>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<div data-controller="nested-form" data-nested-form-single="true">
|
|
2
|
+
<template data-nested-form-target="template">
|
|
3
|
+
<%= form.fields_for field.attribute_name, field.to_model.new do |nested_form| %>
|
|
4
|
+
<%= render partial: field.to_partial_path("fields"), locals: { f: nested_form, field: field } %>
|
|
5
|
+
<% end %>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<% if (object = field.value(record)) %>
|
|
9
|
+
<%= form.fields_for field.attribute_name, object do |nested_form| %>
|
|
10
|
+
<%= render partial: field.to_partial_path("fields"), locals: { f: nested_form, field: field } %>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% end %>
|
|
13
|
+
|
|
14
|
+
<%# Nothing is created until "Add" is clicked, since the template isn't submitted %>
|
|
15
|
+
<%= tag.div data: { nested_form_target: "links" }, hidden: object.present? do %>
|
|
16
|
+
<%= link_to t("madmin.nested_form.add_new"), "#", data: { action: "click->nested-form#add_association" } %>
|
|
17
|
+
<% end %>
|
|
18
|
+
</div>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
<%= form.fields_for field.attribute_name do |pf| %>
|
|
2
|
-
<%= pf.select :value, field.options_for_select(record).map(&:to_global_id), { selected: field.value(record)&.to_global_id, prompt: true }, {
|
|
3
|
-
<%= pf.hidden_field :type, value: "polymorphic" %>
|
|
2
|
+
<%= pf.select :value, field.options_for_select(record).map(&:to_global_id), { selected: field.value(record)&.to_global_id, prompt: true }, { class: "form-input" } %>
|
|
4
3
|
<% end %>
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
<% if (object = field.value(record)) %>
|
|
2
|
-
|
|
2
|
+
<% if (associated_resource = field.associated_resource_for(object)) %>
|
|
3
|
+
<%= link_to_record associated_resource, object %>
|
|
4
|
+
<% else %>
|
|
5
|
+
<%= render partial: "missing_resource", locals: { resource_name: Madmin.resource_name_for(object) } %>
|
|
6
|
+
<% end %>
|
|
3
7
|
<% end %>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<% if (object = field.value(record)) %>
|
|
2
2
|
<% if (associated_resource = field.associated_resource_for(object)) %>
|
|
3
|
-
<%=
|
|
3
|
+
<%= link_to_record associated_resource, object, class: "text-blue-500 underline" %>
|
|
4
4
|
<% else %>
|
|
5
5
|
<%= render partial: "missing_resource", locals: { resource_name: Madmin.resource_name_for(object) } %>
|
|
6
6
|
<% end %>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, {
|
|
1
|
+
<%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, { class: "form-input" } %>
|
data/config/importmap.rb
CHANGED
|
@@ -11,5 +11,4 @@ pin "lexxy", to: "lexxy.js" if defined?(::Lexxy)
|
|
|
11
11
|
pin_all_from Madmin::Engine.root.join("app/javascript/madmin/controllers"), under: "controllers", to: "madmin/controllers"
|
|
12
12
|
pin_all_from Rails.root.join("app/javascript/madmin/controllers"), under: "controllers", to: "madmin/controllers"
|
|
13
13
|
|
|
14
|
-
pin "tom-select", to: "https://ga.jspm.io/npm:tom-select@2.4.1/dist/js/tom-select.complete.js"
|
|
15
14
|
pin "tailwindcss-stimulus-components", to: "https://ga.jspm.io/npm:tailwindcss-stimulus-components@6.1.2/dist/tailwindcss-stimulus-components.module.js"
|
data/config/locales/el.yml
CHANGED
|
@@ -40,3 +40,47 @@ el:
|
|
|
40
40
|
missing_resource:
|
|
41
41
|
message: "Λείπει: %{resource}"
|
|
42
42
|
create_hint: "Δημιουργήστε το εκτελώντας:"
|
|
43
|
+
pagination:
|
|
44
|
+
label: Σελιδοποίηση
|
|
45
|
+
previous: Προηγούμενη
|
|
46
|
+
next: Επόμενη
|
|
47
|
+
info: "Εμφάνιση %{from}-%{to} από %{count}"
|
|
48
|
+
empty: Δεν βρέθηκαν εγγραφές
|
|
49
|
+
filters:
|
|
50
|
+
button: "Φίλτρα"
|
|
51
|
+
heading: "Εμφάνιση εγγραφών όπου"
|
|
52
|
+
add: "+ Προσθήκη φίλτρου"
|
|
53
|
+
apply: "Εφαρμογή"
|
|
54
|
+
clear_all: "Εκκαθάριση φίλτρων"
|
|
55
|
+
remove: "Αφαίρεση φίλτρου"
|
|
56
|
+
column: "Στήλη"
|
|
57
|
+
operator: "Συνθήκη"
|
|
58
|
+
value: "Τιμή"
|
|
59
|
+
operators:
|
|
60
|
+
string:
|
|
61
|
+
contains: "περιέχει"
|
|
62
|
+
eq: "είναι"
|
|
63
|
+
starts_with: "ξεκινά με"
|
|
64
|
+
blank: "είναι κενό"
|
|
65
|
+
present: "δεν είναι κενό"
|
|
66
|
+
number:
|
|
67
|
+
eq: "="
|
|
68
|
+
gt: ">"
|
|
69
|
+
gte: "≥"
|
|
70
|
+
lt: "<"
|
|
71
|
+
lte: "≤"
|
|
72
|
+
blank: "είναι κενό"
|
|
73
|
+
date:
|
|
74
|
+
eq: "στις"
|
|
75
|
+
gte: "στις ή μετά"
|
|
76
|
+
lte: "στις ή πριν"
|
|
77
|
+
blank: "είναι κενό"
|
|
78
|
+
datetime:
|
|
79
|
+
gte: "μετά"
|
|
80
|
+
lte: "πριν"
|
|
81
|
+
blank: "είναι κενό"
|
|
82
|
+
boolean:
|
|
83
|
+
"true": "είναι αληθές"
|
|
84
|
+
"false": "είναι ψευδές"
|
|
85
|
+
array:
|
|
86
|
+
includes: "περιέχει"
|
data/config/locales/en.yml
CHANGED
|
@@ -40,3 +40,47 @@ en:
|
|
|
40
40
|
missing_resource:
|
|
41
41
|
message: "%{resource} is missing."
|
|
42
42
|
create_hint: "Create it by running:"
|
|
43
|
+
pagination:
|
|
44
|
+
label: Pagination
|
|
45
|
+
previous: Previous
|
|
46
|
+
next: Next
|
|
47
|
+
info: "Displaying %{from}-%{to} of %{count}"
|
|
48
|
+
empty: No records found
|
|
49
|
+
filters:
|
|
50
|
+
button: "Filters"
|
|
51
|
+
heading: "Show records where"
|
|
52
|
+
add: "+ Add filter"
|
|
53
|
+
apply: "Apply"
|
|
54
|
+
clear_all: "Clear filters"
|
|
55
|
+
remove: "Remove filter"
|
|
56
|
+
column: "Column"
|
|
57
|
+
operator: "Condition"
|
|
58
|
+
value: "Value"
|
|
59
|
+
operators:
|
|
60
|
+
string:
|
|
61
|
+
contains: "contains"
|
|
62
|
+
eq: "is"
|
|
63
|
+
starts_with: "starts with"
|
|
64
|
+
blank: "is empty"
|
|
65
|
+
present: "is not empty"
|
|
66
|
+
number:
|
|
67
|
+
eq: "="
|
|
68
|
+
gt: ">"
|
|
69
|
+
gte: "≥"
|
|
70
|
+
lt: "<"
|
|
71
|
+
lte: "≤"
|
|
72
|
+
blank: "is empty"
|
|
73
|
+
date:
|
|
74
|
+
eq: "on"
|
|
75
|
+
gte: "on or after"
|
|
76
|
+
lte: "on or before"
|
|
77
|
+
blank: "is empty"
|
|
78
|
+
datetime:
|
|
79
|
+
gte: "after"
|
|
80
|
+
lte: "before"
|
|
81
|
+
blank: "is empty"
|
|
82
|
+
boolean:
|
|
83
|
+
"true": "is true"
|
|
84
|
+
"false": "is false"
|
|
85
|
+
array:
|
|
86
|
+
includes: "includes"
|
data/config/locales/fr.yml
CHANGED
|
@@ -40,3 +40,47 @@ fr:
|
|
|
40
40
|
missing_resource:
|
|
41
41
|
message: "%{resource} est introuvable."
|
|
42
42
|
create_hint: "Pour le créer, exécutez :"
|
|
43
|
+
pagination:
|
|
44
|
+
label: Pagination
|
|
45
|
+
previous: Précédent
|
|
46
|
+
next: Suivant
|
|
47
|
+
info: "Affichage de %{from} à %{to} sur %{count}"
|
|
48
|
+
empty: Aucun enregistrement trouvé
|
|
49
|
+
filters:
|
|
50
|
+
button: "Filtres"
|
|
51
|
+
heading: "Afficher les enregistrements où"
|
|
52
|
+
add: "+ Ajouter un filtre"
|
|
53
|
+
apply: "Appliquer"
|
|
54
|
+
clear_all: "Effacer les filtres"
|
|
55
|
+
remove: "Retirer le filtre"
|
|
56
|
+
column: "Colonne"
|
|
57
|
+
operator: "Condition"
|
|
58
|
+
value: "Valeur"
|
|
59
|
+
operators:
|
|
60
|
+
string:
|
|
61
|
+
contains: "contient"
|
|
62
|
+
eq: "est"
|
|
63
|
+
starts_with: "commence par"
|
|
64
|
+
blank: "est vide"
|
|
65
|
+
present: "n'est pas vide"
|
|
66
|
+
number:
|
|
67
|
+
eq: "="
|
|
68
|
+
gt: ">"
|
|
69
|
+
gte: "≥"
|
|
70
|
+
lt: "<"
|
|
71
|
+
lte: "≤"
|
|
72
|
+
blank: "est vide"
|
|
73
|
+
date:
|
|
74
|
+
eq: "le"
|
|
75
|
+
gte: "le ou après"
|
|
76
|
+
lte: "le ou avant"
|
|
77
|
+
blank: "est vide"
|
|
78
|
+
datetime:
|
|
79
|
+
gte: "après"
|
|
80
|
+
lte: "avant"
|
|
81
|
+
blank: "est vide"
|
|
82
|
+
boolean:
|
|
83
|
+
"true": "est vrai"
|
|
84
|
+
"false": "est faux"
|
|
85
|
+
array:
|
|
86
|
+
includes: "contient"
|
data/config/locales/ko.yml
CHANGED
|
@@ -38,3 +38,47 @@ ko:
|
|
|
38
38
|
missing_resource:
|
|
39
39
|
message: "%{resource}이(가) 존재하지 않습니다."
|
|
40
40
|
create_hint: "다음 명령어를 실행하여 생성하세요:"
|
|
41
|
+
pagination:
|
|
42
|
+
label: 페이지 탐색
|
|
43
|
+
previous: 이전
|
|
44
|
+
next: 다음
|
|
45
|
+
info: "%{count}개 중 %{from}-%{to} 표시"
|
|
46
|
+
empty: 레코드가 없습니다
|
|
47
|
+
filters:
|
|
48
|
+
button: "필터"
|
|
49
|
+
heading: "다음 조건의 레코드 표시"
|
|
50
|
+
add: "+ 필터 추가"
|
|
51
|
+
apply: "적용"
|
|
52
|
+
clear_all: "필터 지우기"
|
|
53
|
+
remove: "필터 제거"
|
|
54
|
+
column: "열"
|
|
55
|
+
operator: "조건"
|
|
56
|
+
value: "값"
|
|
57
|
+
operators:
|
|
58
|
+
string:
|
|
59
|
+
contains: "포함"
|
|
60
|
+
eq: "일치"
|
|
61
|
+
starts_with: "시작"
|
|
62
|
+
blank: "비어 있음"
|
|
63
|
+
present: "비어 있지 않음"
|
|
64
|
+
number:
|
|
65
|
+
eq: "="
|
|
66
|
+
gt: ">"
|
|
67
|
+
gte: "≥"
|
|
68
|
+
lt: "<"
|
|
69
|
+
lte: "≤"
|
|
70
|
+
blank: "비어 있음"
|
|
71
|
+
date:
|
|
72
|
+
eq: "당일"
|
|
73
|
+
gte: "이후(당일 포함)"
|
|
74
|
+
lte: "이전(당일 포함)"
|
|
75
|
+
blank: "비어 있음"
|
|
76
|
+
datetime:
|
|
77
|
+
gte: "이후"
|
|
78
|
+
lte: "이전"
|
|
79
|
+
blank: "비어 있음"
|
|
80
|
+
boolean:
|
|
81
|
+
"true": "참"
|
|
82
|
+
"false": "거짓"
|
|
83
|
+
array:
|
|
84
|
+
includes: "포함"
|
data/lib/madmin/field.rb
CHANGED
|
@@ -6,6 +6,10 @@ module Madmin
|
|
|
6
6
|
to_s.split("::").last.underscore
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
# The kind of index filter a field offers (:string, :number, :date,
|
|
10
|
+
# :datetime or :boolean), or nil for none
|
|
11
|
+
def self.filter_type = nil
|
|
12
|
+
|
|
9
13
|
def initialize(attribute_name:, model:, resource:, options:)
|
|
10
14
|
@attribute_name = attribute_name.to_sym
|
|
11
15
|
@model = model
|
|
@@ -29,8 +33,18 @@ module Madmin
|
|
|
29
33
|
attribute_name
|
|
30
34
|
end
|
|
31
35
|
|
|
36
|
+
# Converts the submitted form value into the value assigned to the record
|
|
37
|
+
def cast(value)
|
|
38
|
+
value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Whether the value returned by `cast` can be saved. The form is shown again with an error when it can't.
|
|
42
|
+
def accepts?(value)
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
32
46
|
def label
|
|
33
|
-
options[:label].presence || attribute_name
|
|
47
|
+
options[:label].presence || model.human_attribute_name(attribute_name)
|
|
34
48
|
end
|
|
35
49
|
|
|
36
50
|
# Used for checking visibility of attribute on an view
|
|
@@ -62,6 +76,18 @@ module Madmin
|
|
|
62
76
|
false
|
|
63
77
|
end
|
|
64
78
|
|
|
79
|
+
def filter_type
|
|
80
|
+
self.class.filter_type if filterable?
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
# Only database columns can be filtered, and not encrypted ones since their
|
|
86
|
+
# stored values can't be compared. `filter: false` turns it off
|
|
87
|
+
def filterable?
|
|
88
|
+
options.fetch(:filter, true) && resource.model_column_names.include?(attribute_name.to_s) && !model.try(:encrypted_attributes)&.include?(attribute_name)
|
|
89
|
+
end
|
|
90
|
+
|
|
65
91
|
ActiveSupport.run_load_hooks(:madmin_field, self)
|
|
66
92
|
end
|
|
67
93
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Madmin
|
|
2
|
+
module Fields
|
|
3
|
+
# PostgreSQL array columns, shown and edited as comma separated values
|
|
4
|
+
class Array < Field
|
|
5
|
+
def self.filter_type = :array
|
|
6
|
+
|
|
7
|
+
def text(record)
|
|
8
|
+
value(record)&.join(", ")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def cast(value)
|
|
12
|
+
value.to_s.split(",").map(&:strip).compact_blank
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
module Madmin
|
|
2
2
|
module Fields
|
|
3
3
|
class Attachment < Field
|
|
4
|
+
# Path for removing the attachment, or nil when the attachment resource
|
|
5
|
+
# or its destroy route has been removed from the app
|
|
6
|
+
def remove_path(attachment)
|
|
7
|
+
resource = Madmin.resource_for(attachment)
|
|
8
|
+
resource.show_path(attachment) if resource.route?(:destroy)
|
|
9
|
+
rescue MissingResource
|
|
10
|
+
end
|
|
4
11
|
end
|
|
5
12
|
end
|
|
6
13
|
end
|
|
@@ -3,7 +3,7 @@ module Madmin
|
|
|
3
3
|
class BelongsTo < Field
|
|
4
4
|
def options_for_select(record)
|
|
5
5
|
current_value = record.send(attribute_name)
|
|
6
|
-
records = [current_value].compact +
|
|
6
|
+
records = [current_value].compact + model.reflect_on_association(attribute_name).klass.excluding(current_value).limit(25)
|
|
7
7
|
records.map { [Madmin.resource_for(_1).display_name(_1), _1.id] }
|
|
8
8
|
end
|
|
9
9
|
|
|
@@ -13,6 +13,7 @@ module Madmin
|
|
|
13
13
|
|
|
14
14
|
def index_path(format: :json)
|
|
15
15
|
associated_resource&.index_path(format: format)
|
|
16
|
+
rescue MissingRoute
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def associated_resource
|