madmin 1.1.0 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +25 -0
  3. data/app/controllers/madmin/base_controller.rb +0 -5
  4. data/app/controllers/madmin/resource_controller.rb +22 -1
  5. data/app/helpers/madmin/application_helper.rb +4 -0
  6. data/app/helpers/madmin/nav_helper.rb +30 -0
  7. data/app/helpers/madmin/sort_helper.rb +32 -0
  8. data/app/views/layouts/madmin/application.html.erb +5 -5
  9. data/app/views/madmin/application/_form.html.erb +6 -8
  10. data/app/views/madmin/application/_javascript.html.erb +35 -4
  11. data/app/views/madmin/application/_navigation.html.erb +31 -5
  12. data/app/views/madmin/application/edit.html.erb +5 -1
  13. data/app/views/madmin/application/index.html.erb +48 -36
  14. data/app/views/madmin/application/new.html.erb +5 -1
  15. data/app/views/madmin/application/show.html.erb +24 -17
  16. data/app/views/madmin/fields/attachment/_form.html.erb +3 -1
  17. data/app/views/madmin/fields/attachment/_show.html.erb +7 -1
  18. data/app/views/madmin/fields/attachments/_form.html.erb +3 -1
  19. data/app/views/madmin/fields/attachments/_show.html.erb +7 -3
  20. data/app/views/madmin/fields/belongs_to/_form.html.erb +4 -2
  21. data/app/views/madmin/fields/belongs_to/_show.html.erb +1 -1
  22. data/app/views/madmin/fields/boolean/_form.html.erb +3 -1
  23. data/app/views/madmin/fields/date/_form.html.erb +3 -1
  24. data/app/views/madmin/fields/date_time/_form.html.erb +3 -1
  25. data/app/views/madmin/fields/decimal/_form.html.erb +3 -1
  26. data/app/views/madmin/fields/enum/_form.html.erb +4 -2
  27. data/app/views/madmin/fields/float/_form.html.erb +3 -1
  28. data/app/views/madmin/fields/has_many/_form.html.erb +4 -2
  29. data/app/views/madmin/fields/has_many/_show.html.erb +1 -1
  30. data/app/views/madmin/fields/has_one/_form.html.erb +3 -2
  31. data/app/views/madmin/fields/integer/_form.html.erb +3 -1
  32. data/app/views/madmin/fields/json/_form.html.erb +3 -1
  33. data/app/views/madmin/fields/nested_has_many/_fields.html.erb +2 -3
  34. data/app/views/madmin/fields/nested_has_many/_form.html.erb +3 -1
  35. data/app/views/madmin/fields/nested_has_many/_show.html.erb +1 -1
  36. data/app/views/madmin/fields/password/_form.html.erb +3 -1
  37. data/app/views/madmin/fields/polymorphic/_form.html.erb +3 -1
  38. data/app/views/madmin/fields/polymorphic/_show.html.erb +1 -1
  39. data/app/views/madmin/fields/rich_text/_form.html.erb +3 -1
  40. data/app/views/madmin/fields/string/_form.html.erb +3 -1
  41. data/app/views/madmin/fields/text/_form.html.erb +3 -1
  42. data/app/views/madmin/fields/time/_form.html.erb +3 -1
  43. data/app/views/madmin/shared/_label.html.erb +4 -0
  44. data/lib/generators/madmin/field/field_generator.rb +31 -0
  45. data/lib/generators/madmin/field/templates/_form.html.erb +2 -0
  46. data/lib/generators/madmin/field/templates/_index.html.erb +1 -0
  47. data/lib/generators/madmin/field/templates/_show.html.erb +1 -0
  48. data/lib/generators/madmin/field/templates/field.rb.tt +26 -0
  49. data/lib/generators/madmin/install/install_generator.rb +6 -1
  50. data/lib/generators/madmin/install/templates/routes.rb.tt +3 -0
  51. data/lib/generators/madmin/resource/resource_generator.rb +9 -59
  52. data/lib/generators/madmin/resource/templates/controller.rb.tt +6 -0
  53. data/lib/generators/madmin/resource/templates/resource.rb.tt +10 -1
  54. data/lib/madmin/engine.rb +4 -4
  55. data/lib/madmin/field.rb +4 -0
  56. data/lib/madmin/fields/belongs_to.rb +9 -6
  57. data/lib/madmin/fields/has_many.rb +10 -6
  58. data/lib/madmin/fields/nested_has_many.rb +1 -1
  59. data/lib/madmin/fields/string.rb +3 -0
  60. data/lib/madmin/fields/text.rb +3 -0
  61. data/lib/madmin/generator_helpers.rb +22 -4
  62. data/lib/madmin/resource.rb +46 -22
  63. data/lib/madmin/resource_builder.rb +80 -0
  64. data/lib/madmin/search.rb +60 -0
  65. data/lib/madmin/version.rb +1 -1
  66. data/lib/madmin.rb +20 -5
  67. metadata +16 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 223251f99312a051f3fd6a6d926c6583347e2c78b255343bc66b3fb063ea8a54
4
- data.tar.gz: cfab141916431f52d137bd7704c33cc4cd3b1781735062e2a9d173f9d9d7bee4
3
+ metadata.gz: 63c8373097cef566d74e69fdd3cefa259506ece6ae8d14a3eae6052f5617cb80
4
+ data.tar.gz: 96e90e7d34a19801137436b2e8310889ac68a959420579d3b746b46bc5158d8d
5
5
  SHA512:
6
- metadata.gz: 7b1480b7df30ec0bba0f1937a7336bb8532f51cd8dc9c8de78fd73b686c132dd14083eec88a303938bfb4fdfa50827db570a3d899e40952799869dfe4795e756
7
- data.tar.gz: e362a3d85149b1f1074ea21eaf6a6659e6c8f002a929f668a21583a286fc1824a2f3ede02997eafe75feaa20e0172345c54210180b7c537b44d0de4bca1eb0b0
6
+ metadata.gz: 342c5a04997ca4ded7f8722559934d0c827aeb078799d743465950e0c04a5cfb7ed112943088bda099faf0251d28079f236cfb3a49f79c43ed949b42ded2cc55
7
+ data.tar.gz: 335b28a9e53d9472c6724b72fc6109ef365e0d4edf732854793c96d3a2f947560ad6dc123307e1ff2e114f2cc126c42008fa1ee3351f5fd6a4452db4b53fb899
data/README.md CHANGED
@@ -79,6 +79,31 @@ rails generate madmin:views:index Book
79
79
  # -> app/views/madmin/books/index.html.erb
80
80
  ```
81
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
+
82
107
  ## Authentication
83
108
 
84
109
  You can use a couple of strategies to authenticate users who are trying to
@@ -3,10 +3,5 @@ module Madmin
3
3
  include Pagy::Backend
4
4
 
5
5
  protect_from_forgery with: :exception
6
-
7
- # Loads all the models for the sidebar
8
- before_action do
9
- Rails.application.eager_load!
10
- end
11
6
  end
12
7
  end
@@ -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
@@ -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
@@ -13,5 +13,9 @@ module Madmin
13
13
  version += "-#{Rails::VERSION::PRE}" if Rails::VERSION::PRE
14
14
  version
15
15
  end
16
+
17
+ def clear_search_params
18
+ params.except(:q, :_page).permit(:page, :sort, :direction)
19
+ end
16
20
  end
17
21
  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="prose" style="max-width:none">
18
- <div class="flex w-full p-4">
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="w-full" role="main">
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[:field].nil? %>
14
- <% next unless attribute[:field].visible?(action_name) %>
15
- <% next unless attribute[:field].visible?(:form) %>
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
- <% field = attribute[:field] %>
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/slim-select@1.27.0/dist/slimselect.min.css", "data-turbo-track": "reload" %>
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
+ 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 stimulusSlimselect from 'https://cdn.skypack.dev/stimulus-slimselect'
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 %>'
@@ -23,6 +25,35 @@
23
25
  import * as Turbo from "https://cdn.skypack.dev/@hotwired/turbo"
24
26
 
25
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
+
26
57
  application.register('nested-form', class extends Controller {
27
58
  static get targets() {
28
59
  return [ "links", "template" ]
@@ -1,6 +1,32 @@
1
- <div class="text-sm">
2
- <%= link_to " Back to App", main_app.root_url, class: "block p-1", data: { turbo: false } if main_app.respond_to?(:root_url) %>
3
- <% Madmin.resources.each do |resource| %>
4
- <%= link_to resource.friendly_name.pluralize, resource.index_path, class: "block p-1" %>
5
- <% end %>
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><%= link_to resource.friendly_name.pluralize, resource.index_path %> / Edit <%= resource.friendly_name %> #<%= @record.id %></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,47 +1,59 @@
1
- <div class="flex justify-between">
2
- <h1><%= resource.friendly_name.pluralize %></h1>
3
- <%= link_to "New #{resource.friendly_name}", resource.new_path %>
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
-
16
- <table class="table-auto">
17
- <thead>
18
- <tr>
19
- <% resource.attributes.each do |attribute| %>
20
- <% next if attribute[:field].nil? %>
21
- <% next unless attribute[:field].visible?(action_name) %>
22
-
23
- <th><%= attribute[:name].to_s.titleize %></th>
24
- <% end %>
25
- <th>Actions</th>
26
- </tr>
27
- </thead>
28
-
29
- <tbody>
30
- <% @records.each do |record| %>
31
- <tr>
32
- <% resource.attributes.each do |attribute| %>
33
- <% next if attribute[:field].nil? %>
34
- <% next unless attribute[:field].visible?(action_name) %>
35
-
36
- <% field = attribute[:field] %>
37
-
38
- <td><%= render partial: field.to_partial_path("index"), locals: { field: field, record: record } %></td>
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">
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
39
  <% end %>
40
-
41
- <td><%= link_to "View", resource.show_path(record) %></td>
40
+ <th class="py-2 px-4 text-left text-xs text-gray-500 font-medium uppercase">Actions</th>
42
41
  </tr>
43
- <% end %>
44
- </tbody>
45
- </table>
46
-
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 } %></td>
51
+ <% end %>
52
+
53
+ <td class="px-4 py-2 text-center"><%= link_to "View", resource.show_path(record), class: "text-indigo-500" %></td>
54
+ </tr>
55
+ <% end %>
56
+ </tbody>
57
+ </table>
58
+ </div>
47
59
  <%== pagy_nav(@pagy) if @pagy.pages > 1 %>
@@ -1,3 +1,7 @@
1
- <h1><%= link_to resource.friendly_name.pluralize, resource.index_path %> / New <%= resource.friendly_name %></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><%= link_to resource.friendly_name.pluralize, resource.index_path %> / <%= resource.model.name.singularize %> #<%= @record.id %></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
- <%= link_to "Edit", resource.edit_path(@record), class: "mr-2" %>
6
- <%= button_to "Delete", resource.show_path(@record), method: :delete, data: { confirm: "Are you sure?" }, class: "inline-block" %>
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
- <% resource.attributes.each do |attribute| %>
11
- <% next if attribute[:field].nil? %>
12
- <% next unless attribute[:field].visible?(action_name) %>
13
- <% field = attribute[:field] %>
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
- <div class="flex py-2">
16
- <div class="w-32 flex-shrink-0 text-gray-700 uppercase tracking-wide text-sm">
17
- <%= field.attribute_name.to_s.titleize %>
18
- </div>
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
- <div>
21
- <%= render partial: field.to_partial_path("show"), locals: { field: field, record: @record } %>
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
- </div>
24
- <% end %>
30
+ <% end %>
31
+ </div>
@@ -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.file_field field.to_param %>
@@ -1,3 +1,9 @@
1
1
  <% if (attachment = field.value(record)) && attachment.attached? %>
2
- <%= link_to attachment.filename, attachment, target: :_blank %>
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
- <%= 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.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
- <div>
4
- <%= link_to attachment.filename, attachment, target: :_blank %>
5
- </div>
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
- <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <%= form.select field.to_param, field.options_for_select(record), { prompt: true }, { class: "form-select", data: { controller: "slimselect", data: { controller: "slimselect" } } } %>
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
- <%= 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.check_box field.attribute_name, class: "form-input" %>
@@ -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-select", data: { controller: "flatpickr" } } %>
@@ -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, data: { controller: "flatpickr", flatpickr_enable_time: true } %>
@@ -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.number_field field.attribute_name, step: :any, class: "form-input" %>
@@ -1,2 +1,4 @@
1
- <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <%= form.select field.attribute_name, field.options_for_select(record), { prompt: true }, { class: "form-select" } %>
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
- <%= 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.number_field field.attribute_name, step: :any, class: "form-input" %>
@@ -1,2 +1,4 @@
1
- <%= form.label field.attribute_name, class: "inline-block w-32 flex-shrink-0" %>
2
- <%= form.select "#{field.attribute_name.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, class: "form-select", data: { controller: "slimselect" } } %>
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.to_s.singularize}_ids", field.options_for_select(record), { prompt: true }, { multiple: true, class: "form-select", data: { controller: "select", select_url_value: field.index_path } } %>
@@ -1,5 +1,5 @@
1
1
  <% field.value(record).each do |object| %>
2
2
  <div>
3
- <%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object) %>
3
+ <%= link_to Madmin.resource_for(object).display_name(object), Madmin.resource_for(object).show_path(object), class: "text-indigo-500 underline" %>
4
4
  </div>
5
5
  <% end %>
@@ -1,3 +1,4 @@
1
- HAS ONE
2
- <%= 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>
3
4
  <%= field.value(record) %>
@@ -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.number_field field.attribute_name, class: "form-input" %>