madmin 1.2.2 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '05151328dcf6fdd8c23755a1b1f3f14b9dcceadc8215e6f102e133d915e95be6'
4
- data.tar.gz: 305bf51253018fa0c871d0314e9ecd69339c510160cdacf1439c2fd361a95a97
3
+ metadata.gz: 79be87e2c5e4bb8b7734ebfac9b623459491a836412bbd0faf077033e6a725ff
4
+ data.tar.gz: a5f93e689c76e156ab396b7da05d47148e19b9ed7bd1f136c4a13b3530c1852a
5
5
  SHA512:
6
- metadata.gz: 625d77d1c5ee5bd30462c2609a8b53b7c5d321fb97e0951a95b852eca019189d468c596fcda77e170ed996bd25707feea787a3668faf318a542a65f03d75e488
7
- data.tar.gz: b594b3c324289e44dbdeb3f0acc70a532b391f8c46faa4854ea5654e9dfe648c88062e24c29433bb476165134a0d79fdbbca526000bbf6c148e9c2c28777f0ed
6
+ metadata.gz: 8bde02b65e86325f630bb2de481a3612e1ae5544891ac3e77bc5ffa78eb158eb6f5603967729040ac408a47f337b42375897848ba89e38a7723ff072dbb97703
7
+ data.tar.gz: c709b74a65fef5383582e3577268dc61e85cb085ce233da6218b586fcf990da32ef5d60cb4991251b3a6b4485f660e018952135233ba23a3bdfeaadfcdbf459a
@@ -0,0 +1,22 @@
1
+ module Madmin
2
+ class ApplicationController < Madmin::BaseController
3
+ before_action :authenticate_admin_user
4
+
5
+ def authenticate_admin_user
6
+ # TODO: Add your authentication logic here
7
+
8
+ # For example, we could redirect if the user isn't an admin
9
+ # redirect_to "/", alert: "Not authorized." unless user_signed_in? && current_user.admin?
10
+ end
11
+
12
+ # Authenticate with Clearance
13
+ # include Clearance::Controller
14
+ # before_action :require_login
15
+
16
+ # Authenticate with Devise
17
+ # before_action :authenticate_user!
18
+
19
+ # Authenticate with Basic Auth
20
+ # http_basic_authenticate_with(name: Rails.application.credentials.admin_username, password: Rails.application.credentials.admin_password)
21
+ end
22
+ end
@@ -1,5 +1,5 @@
1
1
  module Madmin
2
- class DashboardController < ApplicationController
2
+ class DashboardController < Madmin::ApplicationController
3
3
  def show
4
4
  end
5
5
  end
@@ -1,5 +1,5 @@
1
1
  module Madmin
2
- class ResourceController < ApplicationController
2
+ class ResourceController < Madmin::ApplicationController
3
3
  include SortHelper
4
4
 
5
5
  before_action :set_record, except: [:index, :new, :create]
@@ -15,7 +15,7 @@ module Madmin
15
15
  end
16
16
 
17
17
  def clear_search_params
18
- params.except(:q, :_page).permit(:page, :sort, :direction)
18
+ resource.index_path(params.permit(:sort, :direction))
19
19
  end
20
20
  end
21
21
  end
@@ -4,7 +4,7 @@ module Madmin
4
4
  matching_column = (column.to_s == sort_column)
5
5
  direction = sort_direction == "asc" ? "desc" : "asc"
6
6
 
7
- link_to request.params.merge(sort: column, direction: direction), options do
7
+ link_to resource.index_path(sort: column, direction: direction), options do
8
8
  concat title
9
9
  if matching_column
10
10
  concat " "
@@ -3,13 +3,13 @@
3
3
  <%= stylesheet_link_tag "https://unpkg.com/tom-select/dist/css/tom-select.min.css", "data-turbo-track": "reload" %>
4
4
 
5
5
  <script type="module">
6
- import { Application, Controller } from 'https://cdn.skypack.dev/stimulus'
6
+ import { Application, Controller } from 'https://cdn.skypack.dev/@hotwired/stimulus'
7
7
  const application = Application.start()
8
8
 
9
9
  import { Dropdown } from "https://cdn.skypack.dev/tailwindcss-stimulus-components"
10
10
  application.register("dropdown", Dropdown)
11
11
 
12
- import stimulusFlatpickr from 'https://cdn.skypack.dev/stimulus-flatpickr'
12
+ import stimulusFlatpickr from 'https://cdn.skypack.dev/stimulus-flatpickr@3.0.0-0'
13
13
  application.register("flatpickr", stimulusFlatpickr)
14
14
 
15
15
  import TomSelect from 'https://cdn.skypack.dev/tom-select'
@@ -38,7 +38,8 @@
38
38
  labelField: 'name',
39
39
  searchField: 'name',
40
40
  load: (search, callback) => {
41
- fetch(this.urlValue)
41
+ let url = search ? `${this.urlValue}?q=${search}` : this.urlValue;
42
+ fetch(url)
42
43
  .then(response => response.json())
43
44
  .then(json => {
44
45
  callback(json);
@@ -27,33 +27,36 @@
27
27
  <%= link_to scope.to_s.humanize, resource.index_path(scope: scope), class: class_names("p-2 rounded", {"bg-gray-100" => params[:scope] == scope.to_s}) %>
28
28
  <% end %>
29
29
  </div>
30
-
31
- <table class="min-w-full divide-y divide-gray-200">
32
- <thead>
33
- <tr class="border-b border-gray-200">
34
- <% resource.attributes.values.each do |attribute| %>
35
- <% next if attribute.field.nil? %>
36
- <% next unless attribute.field.visible?(action_name) %>
37
-
38
- <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase whitespace-nowrap"><%= sortable attribute.name, attribute.name.to_s.titleize %></th>
39
- <% end %>
40
- <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase">Actions</th>
41
- </tr>
42
- </thead>
43
-
44
- <tbody class="text-sm divide-y">
45
- <% @records.each do |record| %>
46
- <tr>
30
+ <div class="min-w-full max-w-xl overflow-x-auto pb-4">
31
+ <table class="min-w-full divide-y divide-gray-200">
32
+ <thead>
33
+ <tr class="border-b border-gray-200">
47
34
  <% resource.attributes.values.each do |attribute| %>
48
35
  <% next if attribute.field.nil? %>
49
36
  <% next unless attribute.field.visible?(action_name) %>
50
- <td class="px-4 py-2"><%= render partial: attribute.field.to_partial_path("index"), locals: { field: attribute.field, record: record } %></td>
51
- <% end %>
52
37
 
53
- <td class="px-4 py-2 text-center"><%= link_to "View", resource.show_path(record), class: "text-indigo-500" %></td>
38
+ <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase whitespace-nowrap"><%= sortable attribute.name, attribute.name.to_s.titleize %></th>
39
+ <% end %>
40
+ <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase">Actions</th>
54
41
  </tr>
55
- <% end %>
56
- </tbody>
57
- </table>
58
-
42
+ </thead>
43
+
44
+ <tbody class="text-sm divide-y">
45
+ <% @records.each do |record| %>
46
+ <tr>
47
+ <% resource.attributes.values.each do |attribute| %>
48
+ <% next if attribute.field.nil? %>
49
+ <% next unless attribute.field.visible?(action_name) %>
50
+ <td class="px-4 py-2"><%= render partial: attribute.field.to_partial_path("index"), locals: { field: attribute.field, record: record, resource: resource } %></td>
51
+ <% end %>
52
+
53
+ <td class="px-4 py-2 text-center">
54
+ <%= link_to "View", resource.show_path(record), class: "text-indigo-500" %>
55
+ <%= link_to "Edit", resource.edit_path(record), class: "text-indigo-500" %>
56
+ </td>
57
+ </tr>
58
+ <% end %>
59
+ </tbody>
60
+ </table>
61
+ </div>
59
62
  <%== pagy_nav(@pagy) if @pagy.pages > 1 %>
@@ -24,7 +24,7 @@
24
24
  </div>
25
25
 
26
26
  <div class="md:col-span-3">
27
- <%= render partial: attribute.field.to_partial_path("show"), locals: { field: attribute.field, record: @record } %>
27
+ <%= render partial: attribute.field.to_partial_path("show"), locals: { field: attribute.field, record: @record, resource: resource } %>
28
28
  </div>
29
29
  </div>
30
30
  <% end %>
@@ -1,3 +1,3 @@
1
1
  <% if (attachment = field.value(record)) && attachment.attached? %>
2
- <%= link_to attachment.filename, attachment, target: :_blank %>
2
+ <%= link_to attachment.filename, main_app.url_for(attachment), target: :_blank %>
3
3
  <% end %>
@@ -1,9 +1,9 @@
1
1
  <% if (attachment = field.value(record)) && attachment.attached? %>
2
2
  <% if attachment.variable? %>
3
- <%= link_to attachment, target: :_blank do %>
4
- <%= image_tag attachment, class: "max-h-32" %>
3
+ <%= link_to main_app.url_for(attachment), target: :_blank do %>
4
+ <%= image_tag main_app.url_for(attachment), class: "max-h-32" %>
5
5
  <% end %>
6
6
  <% else %>
7
- <%= link_to attachment.filename, attachment, target: :_blank, class: "text-indigo-500 underline" %>
7
+ <%= link_to attachment.filename, main_app.url_for(attachment), target: :_blank, class: "text-indigo-500 underline" %>
8
8
  <% end %>
9
- <% end %>
9
+ <% end %>
@@ -1,11 +1,11 @@
1
1
  <% if (attachments = field.value(record)) && attachments.attached? %>
2
2
  <% attachments.each do |attachment| %>
3
3
  <% if attachment.variable? %>
4
- <%= link_to attachment, target: :_blank do %>
5
- <%= image_tag attachment, class: "max-h-32" %>
4
+ <%= link_to main_app.url_for(attachment), target: :_blank do %>
5
+ <%= image_tag main_app.url_for(attachment), class: "max-h-32" %>
6
6
  <% end %>
7
7
  <% else %>
8
- <%= link_to attachment.filename, attachment, target: :_blank, class: "text-indigo-500 underline" %>
8
+ <%= link_to attachment.filename, main_app.url_for(attachment), target: :_blank, class: "text-indigo-500 underline" %>
9
9
  <% end %>
10
10
  <% end %>
11
11
  <% end %>
@@ -0,0 +1,4 @@
1
+ <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
+ <%= render "madmin/shared/label", form: form, field: field %>
3
+ </div>
4
+ <%= form.file_field field.attribute_name, class: "form-input" %>
@@ -0,0 +1 @@
1
+ <%= field.value(record) %>
@@ -0,0 +1,6 @@
1
+ <% value = field.value(record) %>
2
+ <% if value.respond_to?(:url) %>
3
+ <%= link_to value.url, value.url, target: :_blank, class: "text-indigo-500 underline" %>
4
+ <% else %>
5
+ <%= value %>
6
+ <% end %>
@@ -1 +1,5 @@
1
- <%= field.value(record) %>
1
+ <% if field.attribute_name == :id %>
2
+ <%= link_to field.value(record), resource.show_path(record), class: "text-indigo-500 underline" %>
3
+ <% else %>
4
+ <%= field.value(record) %>
5
+ <% end %>
@@ -1,5 +1,5 @@
1
- <%= content_tag :div, class: "nested-fields bg-gray-100 rounded-t-xl p-5", data: { new_record: f.object.new_record? } do %>
2
- <% field.nested_attributes.each do |nested_attribute| %>
1
+ <%= content_tag :div, class: "nested-fields border border-gray-200 rounded-lg p-5", data: { new_record: f.object.new_record? } do %>
2
+ <% field.nested_attributes.each do |name, nested_attribute| %>
3
3
  <% next if nested_attribute[:field].nil? %>
4
4
  <% next unless nested_attribute[:field].visible?(action_name) %>
5
5
  <% next unless nested_attribute[:field].visible?(:form) %>
@@ -14,5 +14,4 @@
14
14
  <small><%= link_to "Remove", "#", data: { action: "click->nested-form#remove_association" } %></small>
15
15
 
16
16
  <%= f.hidden_field :_destroy %>
17
-
18
17
  <% end %>
@@ -1 +1 @@
1
- <%= field.value(record) %>
1
+ <%= truncate field.value(record).to_s %>
@@ -1,2 +1,4 @@
1
- <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
1
+ <div class="block md:inline-block md:w-32 flex-shrink-0 text-gray-700">
2
+ <%= render "madmin/shared/label", form: form, field: field %>
3
+ </div>
2
4
  <%= form.text_field field.attribute_name, class: "form-input" %>
data/lib/madmin/engine.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  module Madmin
2
2
  class Engine < ::Rails::Engine
3
+ isolate_namespace Madmin
4
+
3
5
  config.before_configuration do |app|
4
- app.config.autoload_paths << File.expand_path("app/madmin/resources", Rails.root)
5
- app.config.autoload_paths << File.expand_path("app/madmin/fields", Rails.root)
6
+ app.config.eager_load_paths << File.expand_path("app/madmin/resources", Rails.root)
7
+ app.config.eager_load_paths << File.expand_path("app/madmin/fields", Rails.root)
6
8
  end
7
9
 
8
10
  config.to_prepare do
data/lib/madmin/field.rb CHANGED
@@ -1,14 +1,15 @@
1
1
  module Madmin
2
2
  class Field
3
- attr_reader :attribute_name, :model, :options
3
+ attr_reader :attribute_name, :model, :options, :resource
4
4
 
5
5
  def self.field_type
6
6
  to_s.split("::").last.underscore
7
7
  end
8
8
 
9
- def initialize(attribute_name:, model:, **options)
9
+ def initialize(attribute_name:, model:, resource:, **options)
10
10
  @attribute_name = attribute_name
11
11
  @model = model
12
+ @resource = resource
12
13
  @options = options
13
14
  end
14
15
 
@@ -0,0 +1,9 @@
1
+ module Madmin
2
+ module Fields
3
+ class File < Field
4
+ def searchable?
5
+ false
6
+ end
7
+ end
8
+ end
9
+ end
@@ -3,7 +3,7 @@ module Madmin
3
3
  class NestedHasMany < Field
4
4
  DEFAULT_ATTRIBUTES = %w[_destroy id].freeze
5
5
  def nested_attributes
6
- resource.attributes.reject { |i| skipped_fields.include?(i[:name]) }
6
+ resource.attributes.reject { |name, attribute| skipped_fields.include?(name) }
7
7
  end
8
8
 
9
9
  def resource
@@ -11,7 +11,7 @@ module Madmin
11
11
  end
12
12
 
13
13
  def model
14
- model_name.constantize
14
+ @model ||= model_name.constantize
15
15
  end
16
16
 
17
17
  def model_find(id)
@@ -37,7 +37,7 @@ module Madmin
37
37
  attributes[name] = OpenStruct.new(
38
38
  name: name,
39
39
  type: type,
40
- field: field.new(**options.merge(attribute_name: name, model: model))
40
+ field: field.new(**options.merge(attribute_name: name, model: model, resource: self))
41
41
  )
42
42
  rescue => e
43
43
  builder = ResourceBuilder.new(model)
@@ -55,31 +55,33 @@ module Madmin
55
55
  end
56
56
 
57
57
  def friendly_name
58
- model_name.gsub("::", " / ")
58
+ model_name.gsub("::", " / ").split(/(?=[A-Z])/).join(" ")
59
59
  end
60
60
 
61
- def index_path(options = {})
62
- route_name = "madmin_#{model.model_name.plural}_path"
61
+ # Support for isolated namespaces
62
+ # Finds parent module class to include in polymorphic urls
63
+ def route_namespace
64
+ return @route_namespace if instance_variable_defined?(:@route_namespace)
65
+ namespace = model.module_parents.detect do |n|
66
+ n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming?
67
+ end
68
+ @route_namespace = (namespace ? namespace.name.singularize.underscore.to_sym : nil)
69
+ end
63
70
 
64
- url_helpers.send(route_name, options)
71
+ def index_path(options = {})
72
+ url_helpers.polymorphic_path([:madmin, route_namespace, model], options)
65
73
  end
66
74
 
67
75
  def new_path
68
- route_name = "new_madmin_#{model.model_name.singular}_path"
69
-
70
- url_helpers.send(route_name)
76
+ url_helpers.polymorphic_path([:madmin, route_namespace, model], action: :new)
71
77
  end
72
78
 
73
79
  def show_path(record)
74
- route_name = "madmin_#{model.model_name.singular}_path"
75
-
76
- url_helpers.send(route_name, record.to_param)
80
+ url_helpers.polymorphic_path([:madmin, route_namespace, record])
77
81
  end
78
82
 
79
83
  def edit_path(record)
80
- route_name = "edit_madmin_#{model.model_name.singular}_path"
81
-
82
- url_helpers.send(route_name, record.to_param)
84
+ url_helpers.polymorphic_path([:madmin, route_namespace, record], action: :edit)
83
85
  end
84
86
 
85
87
  def param_key
@@ -87,7 +89,7 @@ module Madmin
87
89
  end
88
90
 
89
91
  def permitted_params
90
- attributes.values.filter_map { |a| a.field.to_param if a.field.visible?(:form) }
92
+ attributes.values.filter { |a| a.field.visible?(:form) }.map { |a| a.field.to_param }
91
93
  end
92
94
 
93
95
  def display_name(record)
@@ -128,6 +130,7 @@ module Madmin
128
130
  time: Fields::Time,
129
131
  timestamp: Fields::Time,
130
132
  password: Fields::Password,
133
+ file: Fields::File,
131
134
 
132
135
  # Postgres specific types
133
136
  bit: Fields::String,
@@ -1,3 +1,3 @@
1
1
  module Madmin
2
- VERSION = "1.2.2"
2
+ VERSION = "1.2.6"
3
3
  end
data/lib/madmin.rb CHANGED
@@ -18,6 +18,7 @@ module Madmin
18
18
  autoload :DateTime, "madmin/fields/date_time"
19
19
  autoload :Decimal, "madmin/fields/decimal"
20
20
  autoload :Enum, "madmin/fields/enum"
21
+ autoload :File, "madmin/fields/file"
21
22
  autoload :Float, "madmin/fields/float"
22
23
  autoload :HasMany, "madmin/fields/has_many"
23
24
  autoload :HasOne, "madmin/fields/has_one"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: madmin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.2
4
+ version: 1.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Oliver
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-09-23 00:00:00.000000000 Z
12
+ date: 2021-12-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -34,7 +34,7 @@ dependencies:
34
34
  version: '3.5'
35
35
  - - "<"
36
36
  - !ruby/object:Gem::Version
37
- version: '5.0'
37
+ version: '6.0'
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -44,7 +44,7 @@ dependencies:
44
44
  version: '3.5'
45
45
  - - "<"
46
46
  - !ruby/object:Gem::Version
47
- version: '5.0'
47
+ version: '6.0'
48
48
  description: It's an admin, obviously.
49
49
  email:
50
50
  - excid3@gmail.com
@@ -59,6 +59,7 @@ files:
59
59
  - app/assets/config/manifest.js
60
60
  - app/assets/stylesheets/actiontext.scss
61
61
  - app/assets/stylesheets/application.css
62
+ - app/controllers/madmin/application_controller.rb
62
63
  - app/controllers/madmin/base_controller.rb
63
64
  - app/controllers/madmin/dashboard_controller.rb
64
65
  - app/controllers/madmin/resource_controller.rb
@@ -98,6 +99,9 @@ files:
98
99
  - app/views/madmin/fields/enum/_form.html.erb
99
100
  - app/views/madmin/fields/enum/_index.html.erb
100
101
  - app/views/madmin/fields/enum/_show.html.erb
102
+ - app/views/madmin/fields/file/_form.html.erb
103
+ - app/views/madmin/fields/file/_index.html.erb
104
+ - app/views/madmin/fields/file/_show.html.erb
101
105
  - app/views/madmin/fields/float/_form.html.erb
102
106
  - app/views/madmin/fields/float/_index.html.erb
103
107
  - app/views/madmin/fields/float/_show.html.erb
@@ -167,6 +171,7 @@ files:
167
171
  - lib/madmin/fields/date_time.rb
168
172
  - lib/madmin/fields/decimal.rb
169
173
  - lib/madmin/fields/enum.rb
174
+ - lib/madmin/fields/file.rb
170
175
  - lib/madmin/fields/float.rb
171
176
  - lib/madmin/fields/has_many.rb
172
177
  - lib/madmin/fields/has_one.rb
@@ -206,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
211
  - !ruby/object:Gem::Version
207
212
  version: '0'
208
213
  requirements: []
209
- rubygems_version: 3.2.22
214
+ rubygems_version: 3.2.32
210
215
  signing_key:
211
216
  specification_version: 4
212
217
  summary: A modern admin for Ruby on Rails apps