jav_madmin 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +294 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/config/madmin_manifest.js +3 -0
  6. data/app/assets/stylesheets/madmin/actiontext.css +31 -0
  7. data/app/assets/stylesheets/madmin/application-sprockets.css +11 -0
  8. data/app/assets/stylesheets/madmin/application.css +9 -0
  9. data/app/assets/stylesheets/madmin/base.css +144 -0
  10. data/app/assets/stylesheets/madmin/buttons.css +53 -0
  11. data/app/assets/stylesheets/madmin/forms.css +90 -0
  12. data/app/assets/stylesheets/madmin/pagination.css +59 -0
  13. data/app/assets/stylesheets/madmin/reset.css +242 -0
  14. data/app/assets/stylesheets/madmin/sidebar.css +179 -0
  15. data/app/assets/stylesheets/madmin/tables.css +71 -0
  16. data/app/controllers/madmin/application_controller.rb +17 -0
  17. data/app/controllers/madmin/base_controller.rb +15 -0
  18. data/app/controllers/madmin/dashboard_controller.rb +6 -0
  19. data/app/controllers/madmin/resource_controller.rb +119 -0
  20. data/app/helpers/madmin/application_helper.rb +10 -0
  21. data/app/helpers/madmin/nav_helper.rb +34 -0
  22. data/app/helpers/madmin/sort_helper.rb +48 -0
  23. data/app/javascript/madmin/application.js +3 -0
  24. data/app/javascript/madmin/controllers/application.js +12 -0
  25. data/app/javascript/madmin/controllers/index.js +5 -0
  26. data/app/javascript/madmin/controllers/mobile_nav_controller.js +50 -0
  27. data/app/javascript/madmin/controllers/nested_form_controller.js +34 -0
  28. data/app/javascript/madmin/controllers/select_controller.js +37 -0
  29. data/app/views/layouts/madmin/application.html.erb +41 -0
  30. data/app/views/madmin/application/_flash.html.erb +13 -0
  31. data/app/views/madmin/application/_form.html.erb +24 -0
  32. data/app/views/madmin/application/_javascript.html.erb +8 -0
  33. data/app/views/madmin/application/_missing_resource.html.erb +8 -0
  34. data/app/views/madmin/application/_navigation.html.erb +22 -0
  35. data/app/views/madmin/application/edit.html.erb +11 -0
  36. data/app/views/madmin/application/index.html.erb +85 -0
  37. data/app/views/madmin/application/new.html.erb +11 -0
  38. data/app/views/madmin/application/show.html.erb +40 -0
  39. data/app/views/madmin/dashboard/show.html.erb +2 -0
  40. data/app/views/madmin/fields/attachment/_form.html.erb +11 -0
  41. data/app/views/madmin/fields/attachment/_index.html.erb +7 -0
  42. data/app/views/madmin/fields/attachment/_show.html.erb +9 -0
  43. data/app/views/madmin/fields/attachments/_form.html.erb +1 -0
  44. data/app/views/madmin/fields/attachments/_index.html.erb +1 -0
  45. data/app/views/madmin/fields/attachments/_show.html.erb +11 -0
  46. data/app/views/madmin/fields/belongs_to/_form.html.erb +1 -0
  47. data/app/views/madmin/fields/belongs_to/_index.html.erb +4 -0
  48. data/app/views/madmin/fields/belongs_to/_show.html.erb +7 -0
  49. data/app/views/madmin/fields/boolean/_form.html.erb +3 -0
  50. data/app/views/madmin/fields/boolean/_index.html.erb +1 -0
  51. data/app/views/madmin/fields/boolean/_show.html.erb +1 -0
  52. data/app/views/madmin/fields/currency/_form.html.erb +1 -0
  53. data/app/views/madmin/fields/currency/_index.html.erb +1 -0
  54. data/app/views/madmin/fields/currency/_show.html.erb +1 -0
  55. data/app/views/madmin/fields/date/_form.html.erb +1 -0
  56. data/app/views/madmin/fields/date/_index.html.erb +1 -0
  57. data/app/views/madmin/fields/date/_show.html.erb +1 -0
  58. data/app/views/madmin/fields/date_time/_form.html.erb +1 -0
  59. data/app/views/madmin/fields/date_time/_index.html.erb +1 -0
  60. data/app/views/madmin/fields/date_time/_show.html.erb +1 -0
  61. data/app/views/madmin/fields/decimal/_form.html.erb +1 -0
  62. data/app/views/madmin/fields/decimal/_index.html.erb +1 -0
  63. data/app/views/madmin/fields/decimal/_show.html.erb +1 -0
  64. data/app/views/madmin/fields/enum/_form.html.erb +1 -0
  65. data/app/views/madmin/fields/enum/_index.html.erb +1 -0
  66. data/app/views/madmin/fields/enum/_show.html.erb +1 -0
  67. data/app/views/madmin/fields/file/_form.html.erb +1 -0
  68. data/app/views/madmin/fields/file/_index.html.erb +1 -0
  69. data/app/views/madmin/fields/file/_show.html.erb +6 -0
  70. data/app/views/madmin/fields/float/_form.html.erb +1 -0
  71. data/app/views/madmin/fields/float/_index.html.erb +1 -0
  72. data/app/views/madmin/fields/float/_show.html.erb +1 -0
  73. data/app/views/madmin/fields/has_many/_form.html.erb +1 -0
  74. data/app/views/madmin/fields/has_many/_index.html.erb +1 -0
  75. data/app/views/madmin/fields/has_many/_show.html.erb +18 -0
  76. data/app/views/madmin/fields/has_one/_form.html.erb +1 -0
  77. data/app/views/madmin/fields/has_one/_index.html.erb +3 -0
  78. data/app/views/madmin/fields/has_one/_show.html.erb +7 -0
  79. data/app/views/madmin/fields/integer/_form.html.erb +1 -0
  80. data/app/views/madmin/fields/integer/_index.html.erb +1 -0
  81. data/app/views/madmin/fields/integer/_show.html.erb +1 -0
  82. data/app/views/madmin/fields/json/_form.html.erb +1 -0
  83. data/app/views/madmin/fields/json/_index.html.erb +1 -0
  84. data/app/views/madmin/fields/json/_show.html.erb +1 -0
  85. data/app/views/madmin/fields/nested_has_many/_fields.html.erb +17 -0
  86. data/app/views/madmin/fields/nested_has_many/_form.html.erb +28 -0
  87. data/app/views/madmin/fields/nested_has_many/_index.html.erb +1 -0
  88. data/app/views/madmin/fields/nested_has_many/_show.html.erb +18 -0
  89. data/app/views/madmin/fields/password/_form.html.erb +1 -0
  90. data/app/views/madmin/fields/password/_index.html.erb +1 -0
  91. data/app/views/madmin/fields/password/_show.html.erb +1 -0
  92. data/app/views/madmin/fields/polymorphic/_form.html.erb +4 -0
  93. data/app/views/madmin/fields/polymorphic/_index.html.erb +3 -0
  94. data/app/views/madmin/fields/polymorphic/_show.html.erb +7 -0
  95. data/app/views/madmin/fields/rich_text/_form.html.erb +1 -0
  96. data/app/views/madmin/fields/rich_text/_index.html.erb +1 -0
  97. data/app/views/madmin/fields/rich_text/_show.html.erb +3 -0
  98. data/app/views/madmin/fields/select/_form.html.erb +1 -0
  99. data/app/views/madmin/fields/select/_index.html.erb +1 -0
  100. data/app/views/madmin/fields/select/_show.html.erb +1 -0
  101. data/app/views/madmin/fields/string/_form.html.erb +1 -0
  102. data/app/views/madmin/fields/string/_index.html.erb +1 -0
  103. data/app/views/madmin/fields/string/_show.html.erb +1 -0
  104. data/app/views/madmin/fields/text/_form.html.erb +1 -0
  105. data/app/views/madmin/fields/text/_index.html.erb +1 -0
  106. data/app/views/madmin/fields/text/_show.html.erb +1 -0
  107. data/app/views/madmin/fields/time/_form.html.erb +5 -0
  108. data/app/views/madmin/fields/time/_index.html.erb +1 -0
  109. data/app/views/madmin/fields/time/_show.html.erb +1 -0
  110. data/app/views/madmin/shared/_label.html.erb +4 -0
  111. data/config/importmap.rb +15 -0
  112. data/config/locales/el.yml +42 -0
  113. data/config/locales/en.yml +42 -0
  114. data/config/locales/fr.yml +42 -0
  115. data/config/locales/ko.yml +40 -0
  116. data/lib/generators/madmin/field/field_generator.rb +31 -0
  117. data/lib/generators/madmin/field/templates/_form.html.erb +1 -0
  118. data/lib/generators/madmin/field/templates/_index.html.erb +1 -0
  119. data/lib/generators/madmin/field/templates/_show.html.erb +1 -0
  120. data/lib/generators/madmin/field/templates/field.rb.tt +26 -0
  121. data/lib/generators/madmin/install/install_generator.rb +55 -0
  122. data/lib/generators/madmin/install/templates/controller.rb.tt +15 -0
  123. data/lib/generators/madmin/install/templates/routes.rb.tt +3 -0
  124. data/lib/generators/madmin/resource/resource_generator.rb +73 -0
  125. data/lib/generators/madmin/resource/templates/controller.rb.tt +10 -0
  126. data/lib/generators/madmin/resource/templates/resource.rb.tt +33 -0
  127. data/lib/generators/madmin/views/edit_generator.rb +16 -0
  128. data/lib/generators/madmin/views/form_generator.rb +15 -0
  129. data/lib/generators/madmin/views/index_generator.rb +15 -0
  130. data/lib/generators/madmin/views/javascript_generator.rb +15 -0
  131. data/lib/generators/madmin/views/layout_generator.rb +21 -0
  132. data/lib/generators/madmin/views/navigation_generator.rb +15 -0
  133. data/lib/generators/madmin/views/new_generator.rb +16 -0
  134. data/lib/generators/madmin/views/show_generator.rb +15 -0
  135. data/lib/generators/madmin/views/views_generator.rb +16 -0
  136. data/lib/madmin/engine.rb +47 -0
  137. data/lib/madmin/field.rb +67 -0
  138. data/lib/madmin/fields/attachment.rb +6 -0
  139. data/lib/madmin/fields/attachments.rb +9 -0
  140. data/lib/madmin/fields/belongs_to.rb +29 -0
  141. data/lib/madmin/fields/boolean.rb +6 -0
  142. data/lib/madmin/fields/currency.rb +15 -0
  143. data/lib/madmin/fields/date.rb +6 -0
  144. data/lib/madmin/fields/date_time.rb +6 -0
  145. data/lib/madmin/fields/decimal.rb +6 -0
  146. data/lib/madmin/fields/enum.rb +9 -0
  147. data/lib/madmin/fields/file.rb +9 -0
  148. data/lib/madmin/fields/float.rb +6 -0
  149. data/lib/madmin/fields/has_many.rb +62 -0
  150. data/lib/madmin/fields/has_one.rb +10 -0
  151. data/lib/madmin/fields/integer.rb +6 -0
  152. data/lib/madmin/fields/json.rb +6 -0
  153. data/lib/madmin/fields/nested_has_many.rb +44 -0
  154. data/lib/madmin/fields/password.rb +6 -0
  155. data/lib/madmin/fields/polymorphic.rb +22 -0
  156. data/lib/madmin/fields/rich_text.rb +6 -0
  157. data/lib/madmin/fields/select.rb +9 -0
  158. data/lib/madmin/fields/string.rb +9 -0
  159. data/lib/madmin/fields/text.rb +9 -0
  160. data/lib/madmin/fields/time.rb +6 -0
  161. data/lib/madmin/generator_helpers.rb +59 -0
  162. data/lib/madmin/member_action.rb +19 -0
  163. data/lib/madmin/menu.rb +70 -0
  164. data/lib/madmin/namespace.rb +35 -0
  165. data/lib/madmin/resource.rb +295 -0
  166. data/lib/madmin/resource_builder.rb +88 -0
  167. data/lib/madmin/search.rb +62 -0
  168. data/lib/madmin/version.rb +3 -0
  169. data/lib/madmin/view_generator.rb +43 -0
  170. data/lib/madmin.rb +114 -0
  171. data/lib/tasks/madmin_tasks.rake +11 -0
  172. metadata +284 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5d92b6e04ea290f8ea0692b83ed2a324fc76d51cb1c63a35435a34552cdeecba
4
+ data.tar.gz: 24f2b178256dc83dd6c5dd9ed6d276be5b174b9ea34bc2fb4c99549d618488f9
5
+ SHA512:
6
+ metadata.gz: 384f3b0526579449b6dc3e58f3e633ca7d5bb193070a17b052d6d38863f671fc9a84b054b1349912f71d479743b5b7f69f2518e67b693ea7941deb95a55d4b6e
7
+ data.tar.gz: '0293d9d07f98036e8ff5895753aa87549b81345e5356fa8b171fe9bd5fbd3578842669a5c6682fa92efd84683b009eeb997a13dcc095b0e2f71ccab000468f81'
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2020 Chris Oliver
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,294 @@
1
+ # Madmin
2
+
3
+ ### 🛠 A robust Admin Interface for Ruby on Rails apps
4
+
5
+ [![Build Status](https://github.com/excid3/madmin/workflows/Tests/badge.svg)](https://github.com/excid3/madmin/actions) [![Gem Version](https://badge.fury.io/rb/madmin.svg)](https://badge.fury.io/rb/madmin)
6
+
7
+ Why another Ruby on Rails admin? We wanted an admin that was:
8
+
9
+ - Familiar and customizable like Rails scaffolds (less DSL)
10
+ - Supports all the Rails features out of the box (ActionText, ActionMailbox, has_secure_password, etc)
11
+ - Stimulus / Turbolinks / Hotwire ready
12
+ - Works with Import maps and Sprockets
13
+
14
+ ![Madmin Screenshot](docs/images/screenshot.png)
15
+
16
+ ## Installation
17
+
18
+ Add `madmin` to your application's Gemfile:
19
+
20
+ ```bash
21
+ bundle add madmin
22
+ ```
23
+
24
+ Then run the madmin generator:
25
+
26
+ ```bash
27
+ rails g madmin:install
28
+ ```
29
+
30
+ This will install Madmin and generate resources for each of the models it finds.
31
+
32
+ ## Resources
33
+
34
+ Madmin uses `Resource` classes to add models to the admin area.
35
+
36
+ ### Generate a Resource
37
+
38
+ To generate a resource for a model, you can run:
39
+
40
+ ```bash
41
+ rails g madmin:resource ActionText::RichText
42
+ ```
43
+
44
+ ### Avoid N+1 queries
45
+
46
+ In case of N+1 queries, you can preload the association by overriding the `scoped_resource` method in the controller:
47
+
48
+ ```ruby
49
+ module Madmin
50
+ class PostsController < Madmin::ResourceController
51
+ private
52
+
53
+ def scoped_resources
54
+ super.includes(:user)
55
+ end
56
+ end
57
+ end
58
+
59
+ ```
60
+
61
+ ### Read-only Resources
62
+
63
+ To expose a resource in the admin without allowing writes, override `readonly?` in the resource:
64
+
65
+ ```ruby
66
+ class AuditLogResource < Madmin::Resource
67
+ def self.readonly?
68
+ true
69
+ end
70
+ end
71
+ ```
72
+
73
+ The new, edit, and delete links are hidden for read-only resources, and any write actions redirect back to the index.
74
+
75
+ ### Scopes
76
+
77
+ Declare scopes on a resource to render filter buttons on the index page. Each scope must be a scope or class method on the model:
78
+
79
+ ```ruby
80
+ class PostResource < Madmin::Resource
81
+ scope :published
82
+ scope :draft
83
+ end
84
+ ```
85
+
86
+ Button labels default to the humanized scope name. To customize or translate them, define an I18n key — `madmin.scopes.<param_key>.<scope>` for one resource, or `madmin.scopes.<scope>` shared across resources:
87
+
88
+ ```yaml
89
+ en:
90
+ madmin:
91
+ scopes:
92
+ post:
93
+ published: Live
94
+ ```
95
+
96
+ For labels that need logic (e.g. looking up a record name), override `scope_label`:
97
+
98
+ ```ruby
99
+ class PostResource < Madmin::Resource
100
+ def self.scope_label(name)
101
+ name.to_s.titleize
102
+ end
103
+ end
104
+ ```
105
+
106
+ ## Configuring Views
107
+
108
+ The views packaged within the gem are a great starting point, but inevitably people will need to be able to customize those views.
109
+
110
+ You can use the included generator to create the appropriate view files, which can then be customized.
111
+
112
+ For example, running the following will copy over all of the views into your application that will be used for every resource:
113
+
114
+ ```bash
115
+ rails generate madmin:views
116
+ ```
117
+
118
+ The view files that are copied over in this case includes all of the standard Rails action views (index, new, edit, show, and \_form), as well as:
119
+
120
+ - `application.html.erb` (layout file)
121
+ - `_javascript.html.erb` (default JavaScript setup)
122
+ - `_navigation.html.erb` (renders the navigation/sidebar menu)
123
+
124
+ As with the other views, you can specifically run the views generator for only the navigation or application layout views:
125
+
126
+ ```bash
127
+ rails g madmin:views:navigation
128
+ # -> app/views/madmin/_navigation.html.erb
129
+
130
+ rails g madmin:views:layout # Note the layout generator includes the layout, javascript, and navigation files.
131
+ # -> app/views/madmin/application.html.erb
132
+ # -> app/views/madmin/_javascript.html.erb
133
+ # -> app/views/madmin/_navigation.html.erb
134
+ ```
135
+
136
+ If you only need to customize specific views, you can restrict which views are copied by the generator:
137
+
138
+ ```bash
139
+ rails g madmin:views:index
140
+ # -> app/views/madmin/application/index.html.erb
141
+ ```
142
+
143
+ You might want to make some of your model's attributes visible in some views but invisible in others.
144
+ The `attribute` method in model_resource.rb gives you that flexibility.
145
+
146
+ ```bash
147
+ # -> app/madmin/resources/book_resource.rb
148
+ ```
149
+
150
+ ```ruby
151
+ class BookResource < Madmin::Resource
152
+ attribute :id, form: false
153
+ attribute :title
154
+ attribute :subtitle, index: false
155
+ attribute :author
156
+ attribute :genre
157
+ attribute :pages, show: false
158
+ end
159
+ ```
160
+
161
+ You can also scope the copied view(s) to a specific Resource/Model:
162
+
163
+ ```bash
164
+ rails generate madmin:views:index Book
165
+ # -> app/views/madmin/books/index.html.erb
166
+ ```
167
+
168
+ ### Specifying Field Types
169
+
170
+ You can set a field type as the second argument. Field types may have additional options to render the field UI.
171
+
172
+
173
+ For example, we can use a select for the genre attribute and specify the collection of options to choose from.
174
+
175
+ ```ruby
176
+ class BookResource < Madmin::Resource
177
+ attribute :genre, :select, collection: ["Fiction", "Mystery", "Thriller"]
178
+ end
179
+ ```
180
+
181
+ ## Custom Fields
182
+
183
+ You can generate a custom field with:
184
+
185
+ ```bash
186
+ rails g madmin:field Custom
187
+ ```
188
+
189
+ This will create a `CustomField` class in `app/madmin/fields/custom_field.rb`
190
+ And the related views:
191
+
192
+ ```bash
193
+ # -> app/views/madmin/fields/custom_field/_form.html.erb
194
+ # -> app/views/madmin/fields/custom_field/_index.html.erb
195
+ # -> app/views/madmin/fields/custom_field/_show.html.erb
196
+ ```
197
+
198
+ You can then use this field on our resource:
199
+
200
+ ```ruby
201
+ class PostResource < Madmin::Resource
202
+ attribute :title, field: CustomField
203
+ end
204
+ ```
205
+
206
+ ## Actions
207
+
208
+ ### Member Actions
209
+
210
+ `member_action` lets you add custom buttons to a resource's **show** page. Each block receives the current `record` and is rendered in the header's `.actions` div:
211
+
212
+ ```ruby
213
+ class PostResource < Madmin::Resource
214
+ member_action do |record|
215
+ link_to "Publish", publish_admin_post_path(record), class: "btn btn-secondary"
216
+ end
217
+ end
218
+ ```
219
+
220
+ Pass `collection: true` to also render the action on the **index** page, in each row next to the built-in "View" and "Edit" links:
221
+
222
+ ```ruby
223
+ class PostResource < Madmin::Resource
224
+ member_action collection: true do |record|
225
+ link_to "Publish", publish_admin_post_path(record), class: "btn btn-secondary"
226
+ end
227
+ end
228
+ ```
229
+
230
+ ### Collection Actions
231
+
232
+ `collection_action` is the index-page counterpart to `member_action`. Blocks are rendered in the index header's `.actions` div, immediately before the built-in "New \<Resource\>" link.
233
+
234
+ Unlike `member_action`, blocks receive **no** arguments. Each block is `instance_exec`-ed on the index view context, so `link_to`, route helpers, `policy`, `current_user`, `params`, `resource`, and `@records` are all available. Multiple blocks render in registration order.
235
+
236
+ Each block is responsible for its own authorization gating — if a button should be hidden for some users, guard it inside the block.
237
+
238
+ ```ruby
239
+ class PostResource < Madmin::Resource
240
+ collection_action do
241
+ link_to "Bulk Import", bulk_import_admin_posts_path, class: "btn btn-secondary"
242
+ end
243
+
244
+ collection_action do
245
+ link_to "Export CSV", export_admin_posts_path(format: :csv), class: "btn btn-secondary"
246
+ end
247
+ end
248
+ ```
249
+
250
+ ## Extending Madmin
251
+
252
+ Madmin runs `ActiveSupport` load hooks on its core classes so you can extend them from an initializer without reopening or monkey patching them. This is the recommended way for engines and gems to add behavior to Madmin.
253
+
254
+ | Hook | Base | Use it for |
255
+ | --- | --- | --- |
256
+ | `:madmin_resource` | `Madmin::Resource` | Adding to the resource DSL |
257
+ | `:madmin_field` | `Madmin::Field` | Shared behavior across every field type |
258
+ | `:madmin_search` | `Madmin::Search` | Customizing how the search query is built |
259
+ | `:madmin_base_controller` | `Madmin::BaseController` | Layouts, helpers, `rescue_from`, authentication |
260
+ | `:madmin_resource_controller` | `Madmin::ResourceController` | CRUD callbacks, scoping records, audit logging |
261
+
262
+ Blocks are `class_eval`-ed on the base class, so `self` is the class itself:
263
+
264
+ ```ruby
265
+ # config/initializers/madmin.rb
266
+ ActiveSupport.on_load(:madmin_resource) do
267
+ # self == Madmin::Resource, so `extend` adds class-level DSL to every resource
268
+ extend MyAdmin::ResourceExtensions
269
+ end
270
+
271
+ ActiveSupport.on_load(:madmin_base_controller) do
272
+ # self == Madmin::BaseController
273
+ rescue_from Pundit::NotAuthorizedError, with: :admin_not_authorized
274
+ end
275
+ ```
276
+
277
+ Hooks registered after a class has already loaded run immediately, so ordering in your initializers doesn't matter. The controllers live in the engine's `app/` directory and are reloadable, which means their hooks re-run on every reload in development — keep the blocks idempotent.
278
+
279
+ ## Authentication
280
+
281
+ You can use a couple of strategies to authenticate users who are trying to
282
+ access your madmin panel: [Authentication Docs](docs/authentication.md)
283
+
284
+ ## Assets
285
+ You can customize the JavaScript and CSS assets used by Madmin for your application. To learn how
286
+ see the [Assets Doc](docs/assets.md)
287
+
288
+ ## 🙏 Contributing
289
+
290
+ This project uses Standard for formatting Ruby code. Please make sure to run standardrb before submitting pull requests.
291
+
292
+ ## 📝 License
293
+
294
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,30 @@
1
+ begin
2
+ require "bundler/setup"
3
+ rescue LoadError
4
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
5
+ end
6
+
7
+ require "rdoc/task"
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = "rdoc"
11
+ rdoc.title = "Madmin"
12
+ rdoc.options << "--line-numbers"
13
+ rdoc.rdoc_files.include("README.md")
14
+ rdoc.rdoc_files.include("lib/**/*.rb")
15
+ end
16
+
17
+ require "bundler/gem_tasks"
18
+
19
+ require "rake/testtask"
20
+
21
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
22
+ load "rails/tasks/engine.rake"
23
+
24
+ Rake::TestTask.new(:test) do |t|
25
+ t.libs << "test"
26
+ t.pattern = "test/**/*_test.rb"
27
+ t.verbose = false
28
+ end
29
+
30
+ task default: :test
@@ -0,0 +1,3 @@
1
+ //= link_directory ../stylesheets/madmin .css
2
+ //= link_directory ../../javascript/madmin .js
3
+ //= link_tree ../../javascript/madmin/controllers .js
@@ -0,0 +1,31 @@
1
+ /*
2
+ * Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and
3
+ * the trix-editor content (whether displayed or under editing). Feel free to incorporate this
4
+ * inclusion directly in any other asset bundle and remove this file.
5
+ *
6
+ *= require trix
7
+ */
8
+
9
+ /*
10
+ * We need to override trix.css’s image gallery styles to accommodate the
11
+ * <action-text-attachment> element we wrap around attachments. Otherwise,
12
+ * images in galleries will be squished by the max-width: 33%; rule.
13
+ */
14
+ .trix-content .attachment-gallery > action-text-attachment,
15
+ .trix-content .attachment-gallery > .attachment {
16
+ flex: 1 0 33%;
17
+ padding: 0 0.5em;
18
+ max-width: 33%;
19
+ }
20
+
21
+ .trix-content .attachment-gallery.attachment-gallery--2 > action-text-attachment,
22
+ .trix-content .attachment-gallery.attachment-gallery--2 > .attachment, .trix-content .attachment-gallery.attachment-gallery--4 > action-text-attachment,
23
+ .trix-content .attachment-gallery.attachment-gallery--4 > .attachment {
24
+ flex-basis: 50%;
25
+ max-width: 50%;
26
+ }
27
+
28
+ .trix-content action-text-attachment .attachment {
29
+ padding: 0 !important;
30
+ max-width: 100% !important;
31
+ }
@@ -0,0 +1,11 @@
1
+ /*
2
+ *= require trix
3
+ *= require madmin/actiontext
4
+ *= require madmin/reset
5
+ *= require madmin/base
6
+ *= require madmin/sidebar
7
+ *= require madmin/buttons
8
+ *= require madmin/forms
9
+ *= require madmin/tables
10
+ *= require madmin/pagination
11
+ */
@@ -0,0 +1,9 @@
1
+ @import url("/trix.css");
2
+ @import url("/madmin/actiontext.css");
3
+ @import url("/madmin/reset.css");
4
+ @import url("/madmin/base.css");
5
+ @import url("/madmin/sidebar.css");
6
+ @import url("/madmin/buttons.css");
7
+ @import url("/madmin/forms.css");
8
+ @import url("/madmin/tables.css");
9
+ @import url("/madmin/pagination.css");
@@ -0,0 +1,144 @@
1
+ :root {
2
+ --primary-color: rgb(37 99 235);
3
+ --border-color: rgb(229 231 235);
4
+ --background-color: rgb(249 250 251);
5
+ --text-color: rgb(2 6 23);
6
+ --light-text-color: rgb(71 85 105);
7
+ --sidebar-width: 16rem;
8
+ }
9
+
10
+ body {
11
+ color: var(--text-color);
12
+ font-size: 14px;
13
+ -webkit-font-smoothing: antialiased;
14
+ -moz-osx-font-smoothing: grayscale;
15
+
16
+ display: grid;
17
+ min-height: 100dvh;
18
+ grid-template-rows: auto auto 1fr auto;
19
+ }
20
+
21
+ a {
22
+ color: var(--primary-color);
23
+ }
24
+
25
+ .size-5 {
26
+ width: 1.25rem;
27
+ height: 1.25rem;
28
+ }
29
+
30
+ .alert {
31
+ font-weight: 500;
32
+ padding: 1rem;
33
+
34
+ ul {
35
+ margin-top: 0.5rem;
36
+ margin-bottom: 0;
37
+ padding-left: 2rem;
38
+ }
39
+
40
+ svg {
41
+ display: inline-block;
42
+ height: 1rem;
43
+ margin-right: 0.25rem;
44
+ width: 1rem;
45
+ vertical-align: text-bottom;
46
+ }
47
+
48
+ &.alert-danger {
49
+ background-color: oklch(.936 .032 17.717);
50
+ color: oklch(.444 .177 26.899);
51
+
52
+ svg {
53
+ color: oklch(.637 .237 25.331);
54
+ }
55
+ }
56
+
57
+ &.alert-notice {
58
+ background-color: oklch(.962 .044 156.743);
59
+ color: oklch(.448 .119 151.328);
60
+
61
+ svg {
62
+ color: oklch(.723 .219 149.579);
63
+ }
64
+ }
65
+ }
66
+
67
+ .search {
68
+ display: flex;
69
+ align-items: center;
70
+ }
71
+
72
+ .header {
73
+ display: flex;
74
+ flex-wrap: wrap;
75
+ justify-content: space-between;
76
+ align-items: center;
77
+ padding-top: 1rem;
78
+ padding-bottom: 1rem;
79
+ gap: 0.5rem;
80
+
81
+ h1 {
82
+ margin: 0;
83
+ }
84
+
85
+ a {
86
+ text-decoration: none;
87
+
88
+ &:hover {
89
+ text-decoration: underline;
90
+ }
91
+ }
92
+
93
+ .actions {
94
+ align-items: center;
95
+ display: flex;
96
+ flex-wrap: wrap;
97
+ gap: 0.5rem;
98
+ }
99
+ }
100
+
101
+ .metrics {
102
+ display: grid;
103
+ grid-template-columns: repeat(2, minmax(0, 1fr));
104
+
105
+
106
+ .metric {
107
+ border: 1px solid rgb(229 231 235);
108
+ border-radius: 0.25rem;
109
+ padding: 1rem;
110
+ margin: 1rem;
111
+ flex: 1 1 0%;
112
+
113
+ h4 {
114
+ color: rgb(75 85 99);
115
+ font-weight: 600;
116
+ margin-top: 0;
117
+ margin-bottom: 0.5rem;
118
+ }
119
+
120
+ p {
121
+ font-size: 2rem;
122
+ font-weight: 600;
123
+ margin: 0;
124
+ }
125
+ }
126
+ }
127
+
128
+ .scopes {
129
+ margin-bottom: 1rem;
130
+ }
131
+
132
+ .hidden {
133
+ display: none;
134
+ }
135
+
136
+ @media (min-width: 768px) {
137
+ .md\:inline-block {
138
+ display: inline-block;
139
+ }
140
+
141
+ .md\:hidden {
142
+ display: none;
143
+ }
144
+ }
@@ -0,0 +1,53 @@
1
+ .btn {
2
+ border-radius: 0.375rem;
3
+ display: inline-block;
4
+ font-weight: 600;
5
+ text-decoration: none;
6
+
7
+ font-size: 0.875rem;
8
+ line-height: 1.25rem;
9
+ padding-bottom: 0.5rem;
10
+ padding-top: 0.5rem;
11
+ padding-left: 0.75rem;
12
+ padding-right: 0.75rem;
13
+
14
+ &:hover,
15
+ &:focus {
16
+ cursor: pointer;
17
+ text-decoration: none !important;
18
+ }
19
+
20
+ &:disabled {
21
+ opacity: 60%;
22
+ cursor: not-allowed;
23
+ }
24
+
25
+ &.btn-primary {
26
+ background-color: var(--primary-color);
27
+ color: white;
28
+ }
29
+
30
+ &.btn-secondary {
31
+ background-color: white;
32
+ color: rgb(31 41 55);
33
+ box-shadow: inset 0 0 0 1px rgb(156 163 175);
34
+
35
+ &:hover {
36
+ background-color: rgb(243 244 246);
37
+ }
38
+
39
+ &.active {
40
+ background-color: rgb(243 244 246);
41
+ }
42
+ }
43
+
44
+ &.btn-danger {
45
+ background-color: white;
46
+ color: rgb(239 68 68);
47
+ box-shadow: inset 0 0 0 1px rgb(239 68 68);
48
+
49
+ &:hover {
50
+ background-color: rgb(243 244 246);
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,90 @@
1
+ .error {
2
+ color: rgb(239 68 68);
3
+ }
4
+
5
+ .form-hint {
6
+ font-size: 0.875rem;
7
+ margin-top: 0.5rem;
8
+ }
9
+
10
+ .form-group {
11
+ margin-bottom: 1rem;
12
+ }
13
+
14
+ .form-input {
15
+ width: 100%;
16
+ }
17
+
18
+ .form-description {
19
+ color: var(--light-text-color);
20
+ }
21
+
22
+ label {
23
+ color: rgb(17 24 39);
24
+ line-height: 1.5rem;
25
+ font-weight: 500;
26
+ font-size: 0.875rem;
27
+ }
28
+
29
+ button,
30
+ input,
31
+ optgroup,
32
+ select,
33
+ textarea {
34
+ font-family: inherit;
35
+ font-feature-settings: inherit;
36
+ font-variation-settings: inherit;
37
+ font-size: 100%;
38
+ font-weight: inherit;
39
+ line-height: inherit;
40
+ letter-spacing: inherit;
41
+ color: inherit;
42
+ margin: 0;
43
+ padding: 0;
44
+ }
45
+
46
+ [type='text'],
47
+ input:where(:not([type])),
48
+ [type='email'],
49
+ [type='url'],
50
+ [type='password'],
51
+ [type='number'],
52
+ [type='date'],
53
+ [type='datetime-local'],
54
+ [type='month'],
55
+ [type='search'],
56
+ [type='tel'],
57
+ [type='time'],
58
+ [type='week'],
59
+ [multiple],
60
+ textarea,
61
+ select {
62
+ -webkit-appearance: none;
63
+ -moz-appearance: none;
64
+ appearance: none;
65
+ background-color: #fff;
66
+
67
+ border-radius: 0.375rem;
68
+ box-shadow: inset 0 0 0 1px rgb(156 163 175);
69
+ line-height: 1.5rem;
70
+ padding-bottom: 0.375rem;
71
+ padding-left: 0.75rem;
72
+ padding-right: 0.75rem;
73
+ padding-top: 0.375rem;
74
+
75
+ &:focus {
76
+ box-shadow: inset 0 0 0 1px rgb(var(--primary-color));
77
+ }
78
+ }
79
+
80
+ .required {
81
+ color: rgb(239 68 68);
82
+ font-weight: 600;
83
+ }
84
+
85
+ .nested-fields {
86
+ border: 1px solid #e5e7eb;
87
+ border-radius: 0.5rem;
88
+ padding: 1.25rem;
89
+ margin-bottom: 1rem;
90
+ }