madmin 1.0.2 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -1
- data/app/controllers/madmin/base_controller.rb +0 -5
- data/app/controllers/madmin/resource_controller.rb +23 -2
- data/app/helpers/madmin/application_helper.rb +4 -0
- data/app/helpers/madmin/nav_helper.rb +30 -0
- data/app/helpers/madmin/sort_helper.rb +32 -0
- data/app/views/layouts/madmin/application.html.erb +5 -5
- data/app/views/madmin/application/_form.html.erb +6 -8
- data/app/views/madmin/application/_javascript.html.erb +70 -4
- data/app/views/madmin/application/_navigation.html.erb +31 -5
- data/app/views/madmin/application/edit.html.erb +5 -1
- data/app/views/madmin/application/index.html.erb +34 -22
- data/app/views/madmin/application/new.html.erb +5 -1
- data/app/views/madmin/application/show.html.erb +24 -17
- data/app/views/madmin/fields/attachment/_form.html.erb +3 -1
- data/app/views/madmin/fields/attachment/_show.html.erb +7 -1
- data/app/views/madmin/fields/attachments/_form.html.erb +3 -1
- data/app/views/madmin/fields/attachments/_show.html.erb +7 -3
- data/app/views/madmin/fields/belongs_to/_form.html.erb +4 -2
- data/app/views/madmin/fields/belongs_to/_show.html.erb +1 -1
- data/app/views/madmin/fields/boolean/_form.html.erb +3 -1
- data/app/views/madmin/fields/date/_form.html.erb +3 -1
- data/app/views/madmin/fields/date_time/_form.html.erb +3 -1
- data/app/views/madmin/fields/decimal/_form.html.erb +3 -1
- data/app/views/madmin/fields/enum/_form.html.erb +4 -2
- data/app/views/madmin/fields/float/_form.html.erb +3 -1
- data/app/views/madmin/fields/has_many/_form.html.erb +4 -2
- data/app/views/madmin/fields/has_many/_show.html.erb +1 -1
- data/app/views/madmin/fields/has_one/_form.html.erb +3 -2
- data/app/views/madmin/fields/integer/_form.html.erb +3 -1
- data/app/views/madmin/fields/json/_form.html.erb +3 -1
- data/app/views/madmin/fields/nested_has_many/_fields.html.erb +18 -0
- data/app/views/madmin/fields/nested_has_many/_form.html.erb +32 -0
- data/app/views/madmin/fields/nested_has_many/_index.html.erb +1 -0
- data/app/views/madmin/fields/nested_has_many/_show.html.erb +5 -0
- data/app/views/madmin/fields/password/_form.html.erb +4 -0
- data/app/views/madmin/fields/password/_index.html.erb +1 -0
- data/app/views/madmin/fields/password/_show.html.erb +1 -0
- data/app/views/madmin/fields/polymorphic/_form.html.erb +3 -1
- data/app/views/madmin/fields/polymorphic/_show.html.erb +1 -1
- data/app/views/madmin/fields/rich_text/_form.html.erb +3 -1
- data/app/views/madmin/fields/string/_form.html.erb +3 -1
- data/app/views/madmin/fields/text/_form.html.erb +3 -1
- data/app/views/madmin/fields/time/_form.html.erb +3 -1
- data/app/views/madmin/shared/_label.html.erb +4 -0
- data/lib/generators/madmin/field/field_generator.rb +31 -0
- data/lib/generators/madmin/field/templates/_form.html.erb +2 -0
- data/lib/generators/madmin/field/templates/_index.html.erb +1 -0
- data/lib/generators/madmin/field/templates/_show.html.erb +1 -0
- data/lib/generators/madmin/field/templates/field.rb.tt +26 -0
- data/lib/generators/madmin/install/install_generator.rb +6 -1
- data/lib/generators/madmin/install/templates/routes.rb.tt +3 -0
- data/lib/generators/madmin/resource/resource_generator.rb +15 -54
- data/lib/generators/madmin/resource/templates/controller.rb.tt +6 -0
- data/lib/generators/madmin/resource/templates/resource.rb.tt +15 -1
- data/lib/generators/madmin/views/javascript_generator.rb +15 -0
- data/lib/generators/madmin/views/views_generator.rb +6 -5
- data/lib/madmin/engine.rb +4 -4
- data/lib/madmin/field.rb +4 -0
- data/lib/madmin/fields/belongs_to.rb +9 -5
- data/lib/madmin/fields/has_many.rb +10 -5
- data/lib/madmin/fields/nested_has_many.rb +40 -0
- data/lib/madmin/fields/password.rb +6 -0
- data/lib/madmin/fields/string.rb +3 -0
- data/lib/madmin/fields/text.rb +3 -0
- data/lib/madmin/generator_helpers.rb +22 -4
- data/lib/madmin/resource.rb +76 -28
- data/lib/madmin/resource_builder.rb +80 -0
- data/lib/madmin/search.rb +60 -0
- data/lib/madmin/version.rb +1 -1
- data/lib/madmin.rb +31 -14
- metadata +26 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '05151328dcf6fdd8c23755a1b1f3f14b9dcceadc8215e6f102e133d915e95be6'
|
4
|
+
data.tar.gz: 305bf51253018fa0c871d0314e9ecd69339c510160cdacf1439c2fd361a95a97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 625d77d1c5ee5bd30462c2609a8b53b7c5d321fb97e0951a95b852eca019189d468c596fcda77e170ed996bd25707feea787a3668faf318a542a65f03d75e488
|
7
|
+
data.tar.gz: b594b3c324289e44dbdeb3f0acc70a532b391f8c46faa4854ea5654e9dfe648c88062e24c29433bb476165134a0d79fdbbca526000bbf6c148e9c2c28777f0ed
|
data/README.md
CHANGED
@@ -7,9 +7,12 @@
|
|
7
7
|
Why another Ruby on Rails admin? We wanted an admin that was:
|
8
8
|
|
9
9
|
* Familiar and customizable like Rails scaffolds (less DSL)
|
10
|
-
* Supports all the Rails features out of the box (ActionText, ActionMailbox, etc)
|
10
|
+
* Supports all the Rails features out of the box (ActionText, ActionMailbox, has_secure_password, etc)
|
11
11
|
* Stimulus / Turbolinks / Hotwire ready
|
12
12
|
|
13
|
+
![Madmin Screenshot](docs/images/screenshot.png)
|
14
|
+
_We're still working on the design!_
|
15
|
+
|
13
16
|
## Installation
|
14
17
|
Add `madmin` to your application's Gemfile:
|
15
18
|
|
@@ -76,6 +79,31 @@ rails generate madmin:views:index Book
|
|
76
79
|
# -> app/views/madmin/books/index.html.erb
|
77
80
|
```
|
78
81
|
|
82
|
+
## Custom Fields
|
83
|
+
|
84
|
+
You can generate a custom field with:
|
85
|
+
|
86
|
+
```bash
|
87
|
+
rails g madmin:field Custom
|
88
|
+
```
|
89
|
+
|
90
|
+
This will create a `CustomField` class in `app/madmin/fields/custom_field.rb`
|
91
|
+
And the related views:
|
92
|
+
|
93
|
+
```bash
|
94
|
+
# -> app/views/madmin/fields/custom_field/_form.html.erb
|
95
|
+
# -> app/views/madmin/fields/custom_field/_index.html.erb
|
96
|
+
# -> app/views/madmin/fields/custom_field/_show.html.erb
|
97
|
+
```
|
98
|
+
|
99
|
+
You can then use this field on our resource:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
class PostResource < Madmin::Resource
|
103
|
+
attribute :title, field: CustomField
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
79
107
|
## Authentication
|
80
108
|
|
81
109
|
You can use a couple of strategies to authenticate users who are trying to
|
@@ -1,9 +1,24 @@
|
|
1
1
|
module Madmin
|
2
2
|
class ResourceController < ApplicationController
|
3
|
+
include SortHelper
|
4
|
+
|
3
5
|
before_action :set_record, except: [:index, :new, :create]
|
4
6
|
|
7
|
+
# Assign current_user for paper_trail gem
|
8
|
+
before_action :set_paper_trail_whodunnit, if: -> { respond_to?(:set_paper_trail_whodunnit, true) }
|
9
|
+
|
5
10
|
def index
|
6
11
|
@pagy, @records = pagy(scoped_resources)
|
12
|
+
|
13
|
+
respond_to do |format|
|
14
|
+
format.html
|
15
|
+
format.json {
|
16
|
+
render json: @records.map { |r| {name: @resource.display_name(r), id: r.id} }
|
17
|
+
}
|
18
|
+
end
|
19
|
+
rescue Pagy::OverflowError
|
20
|
+
params[:page] = 1
|
21
|
+
retry
|
7
22
|
end
|
8
23
|
|
9
24
|
def show
|
@@ -41,7 +56,7 @@ module Madmin
|
|
41
56
|
private
|
42
57
|
|
43
58
|
def set_record
|
44
|
-
@record = resource.
|
59
|
+
@record = resource.model_find(params[:id])
|
45
60
|
end
|
46
61
|
|
47
62
|
def resource
|
@@ -54,7 +69,9 @@ module Madmin
|
|
54
69
|
end
|
55
70
|
|
56
71
|
def scoped_resources
|
57
|
-
resource.model.send(valid_scope)
|
72
|
+
resources = resource.model.send(valid_scope)
|
73
|
+
resources = Madmin::Search.new(resources, resource, search_term).run
|
74
|
+
resources.reorder(sort_column => sort_direction)
|
58
75
|
end
|
59
76
|
|
60
77
|
def valid_scope
|
@@ -77,5 +94,9 @@ module Madmin
|
|
77
94
|
raise "Unrecognised param data: #{data.inspect}"
|
78
95
|
end
|
79
96
|
end
|
97
|
+
|
98
|
+
def search_term
|
99
|
+
@search_term ||= params[:q].to_s.strip
|
100
|
+
end
|
80
101
|
end
|
81
102
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Madmin::NavHelper
|
2
|
+
def nav_link_to(name = nil, options = {}, html_options = {}, &block)
|
3
|
+
if block
|
4
|
+
html_options = options
|
5
|
+
options = name
|
6
|
+
name = block
|
7
|
+
end
|
8
|
+
|
9
|
+
url = url_for(options)
|
10
|
+
starts_with = html_options.delete(:starts_with)
|
11
|
+
html_options[:class] = Array.wrap(html_options[:class])
|
12
|
+
active_class = html_options.delete(:active_class) || "active"
|
13
|
+
inactive_class = html_options.delete(:inactive_class) || ""
|
14
|
+
|
15
|
+
active = if (paths = Array.wrap(starts_with)) && paths.present?
|
16
|
+
paths.any? { |path| request.path.start_with?(path) }
|
17
|
+
else
|
18
|
+
request.path == url
|
19
|
+
end
|
20
|
+
|
21
|
+
classes = active ? active_class : inactive_class
|
22
|
+
html_options[:class] << classes unless classes.empty?
|
23
|
+
|
24
|
+
html_options.except!(:class) if html_options[:class].empty?
|
25
|
+
|
26
|
+
return link_to url, html_options, &block if block
|
27
|
+
|
28
|
+
link_to name, url, html_options
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Madmin
|
2
|
+
module SortHelper
|
3
|
+
def sortable(column, title, options = {})
|
4
|
+
matching_column = (column.to_s == sort_column)
|
5
|
+
direction = sort_direction == "asc" ? "desc" : "asc"
|
6
|
+
|
7
|
+
link_to request.params.merge(sort: column, direction: direction), options do
|
8
|
+
concat title
|
9
|
+
if matching_column
|
10
|
+
concat " "
|
11
|
+
concat tag.i(sort_direction == "asc" ? "▲" : "▼")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def sort_column
|
17
|
+
resource.sortable_columns.include?(params[:sort]) ? params[:sort] : default_sort_column
|
18
|
+
end
|
19
|
+
|
20
|
+
def sort_direction
|
21
|
+
["asc", "desc"].include?(params[:direction]) ? params[:direction] : default_sort_direction
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_sort_column
|
25
|
+
resource.try(:default_sort_column) || (["created_at", "id", "uuid"] & resource.model.column_names).first
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_sort_direction
|
29
|
+
resource.try(:default_sort_direction) || "desc"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -7,19 +7,19 @@
|
|
7
7
|
<title>
|
8
8
|
Madmin: <%= Rails.application.class %>
|
9
9
|
</title>
|
10
|
-
<link href="https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" rel="stylesheet" />
|
11
10
|
<link href="https://unpkg.com/@tailwindcss/forms/dist/forms.min.css" rel="stylesheet" />
|
11
|
+
<link href="https://unpkg.com/tailwindcss@^2.0/dist/tailwind.min.css" rel="stylesheet" />
|
12
12
|
<link href="https://unpkg.com/@tailwindcss/typography/dist/typography.min.css" rel="stylesheet" />
|
13
13
|
<%= csrf_meta_tags %>
|
14
14
|
|
15
15
|
<%= render "javascript" %>
|
16
16
|
</head>
|
17
|
-
<body class="
|
18
|
-
<div class="flex w-full
|
19
|
-
<div id="sidebar" class="w-64 flex-shrink-0">
|
17
|
+
<body class="min-h-screen">
|
18
|
+
<div class="md:flex w-full min-h-screen">
|
19
|
+
<div id="sidebar" class="md:w-64 p-4 flex-shrink-0 border-r">
|
20
20
|
<%= render "navigation" -%>
|
21
21
|
</div>
|
22
|
-
<main class="
|
22
|
+
<main class="flex-grow p-4 overflow-x-scroll" role="main">
|
23
23
|
<%#= render "flashes" -%>
|
24
24
|
<%= yield %>
|
25
25
|
</main>
|
@@ -9,15 +9,13 @@
|
|
9
9
|
</div>
|
10
10
|
<% end %>
|
11
11
|
|
12
|
-
<% resource.attributes.each do |attribute| %>
|
13
|
-
<% next if attribute
|
14
|
-
<% next unless attribute
|
15
|
-
<% next unless attribute
|
12
|
+
<% resource.attributes.values.each do |attribute| %>
|
13
|
+
<% next if attribute.field.nil? %>
|
14
|
+
<% next unless attribute.field.visible?(action_name) %>
|
15
|
+
<% next unless attribute.field.visible?(:form) %>
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
<div class="mb-4 flex">
|
20
|
-
<%= render partial: field.to_partial_path("form"), locals: { field: field, record: record, form: form, resource: resource } %>
|
17
|
+
<div class="mb-4 md:flex">
|
18
|
+
<%= render partial: attribute.field.to_partial_path("form"), locals: { field: attribute.field, record: record, form: form, resource: resource } %>
|
21
19
|
</div>
|
22
20
|
<% end %>
|
23
21
|
|
@@ -1,16 +1,18 @@
|
|
1
1
|
<%= stylesheet_link_tag "https://unpkg.com/flatpickr/dist/flatpickr.min.css", "data-turbo-track": "reload" %>
|
2
2
|
<%= stylesheet_link_tag "https://unpkg.com/trix/dist/trix.css", "data-turbo-track": "reload" %>
|
3
|
-
<%= stylesheet_link_tag "https://unpkg.com/
|
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 } from 'https://cdn.skypack.dev/stimulus'
|
6
|
+
import { Application, Controller } from 'https://cdn.skypack.dev/stimulus'
|
7
7
|
const application = Application.start()
|
8
8
|
|
9
|
+
import { Dropdown } from "https://cdn.skypack.dev/tailwindcss-stimulus-components"
|
10
|
+
application.register("dropdown", Dropdown)
|
11
|
+
|
9
12
|
import stimulusFlatpickr from 'https://cdn.skypack.dev/stimulus-flatpickr'
|
10
13
|
application.register("flatpickr", stimulusFlatpickr)
|
11
14
|
|
12
|
-
import
|
13
|
-
application.register("slimselect", stimulusSlimselect)
|
15
|
+
import TomSelect from 'https://cdn.skypack.dev/tom-select'
|
14
16
|
|
15
17
|
import Rails from 'https://cdn.skypack.dev/@rails/ujs@<%= npm_rails_version %>'
|
16
18
|
import * as ActiveStorage from 'https://cdn.skypack.dev/@rails/activestorage@<%= npm_rails_version %>'
|
@@ -21,4 +23,68 @@
|
|
21
23
|
ActiveStorage.start()
|
22
24
|
|
23
25
|
import * as Turbo from "https://cdn.skypack.dev/@hotwired/turbo"
|
26
|
+
|
27
|
+
(() => {
|
28
|
+
application.register('select', class extends Controller {
|
29
|
+
static values = {
|
30
|
+
options: Object,
|
31
|
+
url: String
|
32
|
+
}
|
33
|
+
|
34
|
+
connect() {
|
35
|
+
this.select = new TomSelect(this.element, {
|
36
|
+
plugins: ['remove_button'],
|
37
|
+
valueField: 'id',
|
38
|
+
labelField: 'name',
|
39
|
+
searchField: 'name',
|
40
|
+
load: (search, callback) => {
|
41
|
+
fetch(this.urlValue)
|
42
|
+
.then(response => response.json())
|
43
|
+
.then(json => {
|
44
|
+
callback(json);
|
45
|
+
}).catch(() => {
|
46
|
+
callback();
|
47
|
+
});
|
48
|
+
}
|
49
|
+
})
|
50
|
+
}
|
51
|
+
|
52
|
+
disconnect() {
|
53
|
+
this.select.destroy()
|
54
|
+
}
|
55
|
+
})
|
56
|
+
|
57
|
+
application.register('nested-form', class extends Controller {
|
58
|
+
static get targets() {
|
59
|
+
return [ "links", "template" ]
|
60
|
+
}
|
61
|
+
|
62
|
+
connect() {
|
63
|
+
this.wrapperClass = this.data.get("wrapperClass") || "nested-fields"
|
64
|
+
}
|
65
|
+
|
66
|
+
add_association(event) {
|
67
|
+
event.preventDefault()
|
68
|
+
|
69
|
+
var content = this.templateTarget.innerHTML.replace(/NEW_RECORD/g, new Date().getTime())
|
70
|
+
this.linksTarget.insertAdjacentHTML('beforebegin', content)
|
71
|
+
}
|
72
|
+
|
73
|
+
remove_association(event) {
|
74
|
+
event.preventDefault()
|
75
|
+
|
76
|
+
let wrapper = event.target.closest("." + this.wrapperClass)
|
77
|
+
|
78
|
+
// New records are simply removed from the page
|
79
|
+
if (wrapper.dataset.newRecord == "true") {
|
80
|
+
wrapper.remove()
|
81
|
+
|
82
|
+
// Existing records are hidden and flagged for deletion
|
83
|
+
} else {
|
84
|
+
wrapper.querySelector("input[name*='_destroy']").value = 1
|
85
|
+
wrapper.style.display = 'none'
|
86
|
+
}
|
87
|
+
}
|
88
|
+
})
|
89
|
+
})()
|
24
90
|
</script>
|
@@ -1,6 +1,32 @@
|
|
1
|
-
<div class="text-sm">
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
<div class="flex flex-col h-full text-sm" data-controller="dropdown">
|
2
|
+
<div class="flex md:block justify-between items-center">
|
3
|
+
<div class="flex md:block items-center">
|
4
|
+
<h1 class="mr-2 md:p-2 text-xl font-semibold">Madmin</h1>
|
5
|
+
<% if main_app.respond_to?(:root_url) %>
|
6
|
+
<%= link_to main_app.root_url, class: "block p-2 rounded hover:bg-gray-200", data: { turbo: false } do %>
|
7
|
+
← Back <span class="hidden md:inline">to App</span>
|
8
|
+
<% end %>
|
9
|
+
<% end %>
|
10
|
+
</div>
|
11
|
+
|
12
|
+
<div class="-mr-2 flex items-center md:hidden">
|
13
|
+
<button data-action="click->dropdown#toggle touch->dropdown#toggle click@window->dropdown#hide touch@window#dropdown->hide" type="button" class="bg-white rounded-md p-2 inline-flex items-center justify-center text-gray-400 hover:bg-gray-200 focus:outline-none focus:ring-2 focus-ring-inset focus:ring-white" id="main-menu" aria-haspopup="true">
|
14
|
+
<span class="sr-only">Open main menu</span>
|
15
|
+
<!-- Heroicon name: outline/menu -->
|
16
|
+
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
17
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
18
|
+
</svg>
|
19
|
+
</button>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
|
23
|
+
<div class="hidden md:flex flex-col flex-grow justify-between" data-dropdown-target="menu">
|
24
|
+
<% Madmin.resources.each do |resource| %>
|
25
|
+
<%= nav_link_to resource.friendly_name.pluralize, resource.index_path, class: "block p-2 rounded hover:bg-gray-100", starts_with: resource.index_path, active_class: "font-bold text-black" %>
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
<div class="mt-auto">
|
29
|
+
<%= link_to "View Madmin on GitHub", "https://github.com/excid3/madmin", target: :_blank, class: "block p-2 rounded text-gray-500 hover:bg-gray-100" %>
|
30
|
+
</div>
|
31
|
+
</div>
|
6
32
|
</div>
|
@@ -1,3 +1,7 @@
|
|
1
|
-
<h1
|
1
|
+
<h1 class="text-xl mb-4">
|
2
|
+
<%= link_to resource.friendly_name.pluralize, resource.index_path, class: "text-indigo-500" %>
|
3
|
+
/
|
4
|
+
<strong>Edit <%= link_to resource.display_name(@record), resource.show_path(@record), class: "text-indigo-500" %></strong>
|
5
|
+
</h1>
|
2
6
|
|
3
7
|
<%= render partial: "form", locals: { record: @record, resource: resource } %>
|
@@ -1,44 +1,56 @@
|
|
1
|
-
<div class="flex justify-between">
|
2
|
-
<h1><%= resource.friendly_name.pluralize %></h1>
|
3
|
-
|
1
|
+
<div class="md:flex justify-between items-center space-y-4 md:space-y-0">
|
2
|
+
<h1 class="text-xl font-semibold"><%= resource.friendly_name.pluralize %></h1>
|
3
|
+
|
4
|
+
<div class="flex-grow flex md:justify-end gap-4">
|
5
|
+
<form class="flex items-center gap-2 relative">
|
6
|
+
<%= hidden_field_tag :page, params[:page], value: 1, class: "hidden" %>
|
7
|
+
<%= search_field_tag :q, params[:q], placeholder: "Search", class: "rounded-full px-4 focus:bg-white focus:border-indigo-500" %>
|
8
|
+
<%= link_to clear_search_params, class: "absolute top-1/2 right-3 text-gray-500 bg-white transform -translate-y-1/2" do %>
|
9
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
10
|
+
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
|
11
|
+
</svg>
|
12
|
+
<% end %>
|
13
|
+
</form>
|
14
|
+
|
15
|
+
<%= link_to resource.new_path, class: "bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow" do %>
|
16
|
+
New <span class="hidden md:inline"><%= resource.friendly_name %></span>
|
17
|
+
<% end %>
|
18
|
+
</div>
|
4
19
|
</div>
|
5
20
|
|
6
|
-
<div>
|
21
|
+
<div class="mb-4">
|
7
22
|
<% if resource.scopes.any? %>
|
8
|
-
<%= link_to "All", resource.index_path %>
|
23
|
+
<%= link_to "All", resource.index_path, class: class_names("p-2 rounded", {"bg-gray-100" => params[:scope].blank?}) %>
|
9
24
|
<% end %>
|
10
25
|
|
11
26
|
<% resource.scopes.each do |scope| %>
|
12
|
-
<%= link_to scope.to_s.humanize, resource.index_path(scope: scope) %>
|
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}) %>
|
13
28
|
<% end %>
|
14
29
|
</div>
|
15
30
|
|
16
|
-
<table class="
|
31
|
+
<table class="min-w-full divide-y divide-gray-200">
|
17
32
|
<thead>
|
18
|
-
<tr>
|
19
|
-
<% resource.attributes.each do |attribute| %>
|
20
|
-
<% next if attribute
|
21
|
-
<% next unless attribute
|
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) %>
|
22
37
|
|
23
|
-
<th><%= attribute
|
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>
|
24
39
|
<% end %>
|
25
|
-
<th>Actions</th>
|
40
|
+
<th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase">Actions</th>
|
26
41
|
</tr>
|
27
42
|
</thead>
|
28
43
|
|
29
|
-
<tbody>
|
44
|
+
<tbody class="text-sm divide-y">
|
30
45
|
<% @records.each do |record| %>
|
31
46
|
<tr>
|
32
|
-
<% resource.attributes.each do |attribute| %>
|
33
|
-
<% next if attribute
|
34
|
-
<% next unless attribute
|
35
|
-
|
36
|
-
<% field = attribute[:field] %>
|
37
|
-
|
38
|
-
<td><%= render partial: field.to_partial_path("index"), locals: { field: field, record: record } %></td>
|
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 } %></td>
|
39
51
|
<% end %>
|
40
52
|
|
41
|
-
<td><%= link_to "View", resource.show_path(record) %></td>
|
53
|
+
<td class="px-4 py-2 text-center"><%= link_to "View", resource.show_path(record), class: "text-indigo-500" %></td>
|
42
54
|
</tr>
|
43
55
|
<% end %>
|
44
56
|
</tbody>
|
@@ -1,3 +1,7 @@
|
|
1
|
-
<h1
|
1
|
+
<h1 class="text-xl mb-4">
|
2
|
+
<%= link_to resource.friendly_name.pluralize, resource.index_path, class: "text-indigo-500" %>
|
3
|
+
/
|
4
|
+
<strong>New <%= resource.friendly_name %></strong>
|
5
|
+
</h1>
|
2
6
|
|
3
7
|
<%= render partial: "form", locals: { record: @record, resource: resource } %>
|
@@ -1,24 +1,31 @@
|
|
1
|
-
<div class="flex justify-between">
|
2
|
-
<h1
|
1
|
+
<div class="md:flex items-center justify-between mb-4">
|
2
|
+
<h1 class="text-xl">
|
3
|
+
<%= link_to resource.friendly_name.pluralize, resource.index_path, class: "text-indigo-500" %>
|
4
|
+
/
|
5
|
+
<%= link_to resource.display_name(@record), resource.show_path(@record), class: "text-indigo-500 font-bold" %>
|
6
|
+
</h1>
|
3
7
|
|
4
|
-
<div class="flex px-4">
|
5
|
-
|
6
|
-
|
8
|
+
<div class="flex items-center px-4">
|
9
|
+
<div class="mr-2">
|
10
|
+
<%= link_to "Edit", resource.edit_path(@record), class: "block bg-white hover:bg-gray-100 text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow" %>
|
11
|
+
</div>
|
12
|
+
<%= button_to "Delete", resource.show_path(@record), method: :delete, data: { confirm: "Are you sure?" }, class: "bg-white hover:bg-gray-100 text-red-500 font-semibold py-2 px-4 border border-red-500 rounded shadow pointer-cursor" %>
|
7
13
|
</div>
|
8
14
|
</div>
|
9
15
|
|
10
|
-
|
11
|
-
<%
|
12
|
-
|
13
|
-
|
16
|
+
<div class="divide-y">
|
17
|
+
<% resource.attributes.values.each do |attribute| %>
|
18
|
+
<% next if attribute.field.nil? %>
|
19
|
+
<% next unless attribute.field.visible?(action_name) %>
|
14
20
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
21
|
+
<div class="px-4 py-3 md:grid md:grid-cols-4 md:gap-4 md:px-6">
|
22
|
+
<div class="text-sm font-medium text-gray-500">
|
23
|
+
<%= attribute.field.attribute_name.to_s.titleize %>
|
24
|
+
</div>
|
19
25
|
|
20
|
-
|
21
|
-
|
26
|
+
<div class="md:col-span-3">
|
27
|
+
<%= render partial: attribute.field.to_partial_path("show"), locals: { field: attribute.field, record: @record } %>
|
28
|
+
</div>
|
22
29
|
</div>
|
23
|
-
|
24
|
-
|
30
|
+
<% end %>
|
31
|
+
</div>
|
@@ -1,3 +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" %>
|
5
|
+
<% end %>
|
6
|
+
<% else %>
|
7
|
+
<%= link_to attachment.filename, attachment, target: :_blank, class: "text-indigo-500 underline" %>
|
8
|
+
<% end %>
|
3
9
|
<% end %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
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.file_field field.attribute_name, multiple: true %>
|
@@ -1,7 +1,11 @@
|
|
1
1
|
<% if (attachments = field.value(record)) && attachments.attached? %>
|
2
2
|
<% attachments.each do |attachment| %>
|
3
|
-
|
4
|
-
<%= link_to attachment
|
5
|
-
|
3
|
+
<% if attachment.variable? %>
|
4
|
+
<%= link_to attachment, target: :_blank do %>
|
5
|
+
<%= image_tag attachment, class: "max-h-32" %>
|
6
|
+
<% end %>
|
7
|
+
<% else %>
|
8
|
+
<%= link_to attachment.filename, attachment, target: :_blank, class: "text-indigo-500 underline" %>
|
9
|
+
<% end %>
|
6
10
|
<% end %>
|
7
11
|
<% end %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
2
|
-
<%=
|
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.select field.to_param, field.options_for_select(record), { prompt: true }, { class: "form-select", data: { controller: "select", select_url_value: field.index_path } } %>
|
@@ -1,3 +1,3 @@
|
|
1
1
|
<% if (object = field.value(record)) %>
|
2
|
-
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
|
2
|
+
<%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object), class: "text-indigo-500 underline" %>
|
3
3
|
<% end %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
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.check_box field.attribute_name, class: "form-input" %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
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-select", data: { controller: "flatpickr" } } %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
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, data: { controller: "flatpickr", flatpickr_enable_time: true } %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
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.number_field field.attribute_name, step: :any, class: "form-input" %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
2
|
-
<%=
|
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.select field.attribute_name, field.options_for_select(record), { prompt: true }, { class: "form-select", data: { controller: "select" } } %>
|
@@ -1,2 +1,4 @@
|
|
1
|
-
|
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.number_field field.attribute_name, step: :any, class: "form-input" %>
|