admin_suite 0.2.0 → 0.2.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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/admin_suite.css +128 -0
  3. data/app/controllers/admin_suite/application_controller.rb +32 -2
  4. data/app/controllers/admin_suite/dashboard_controller.rb +59 -226
  5. data/app/helpers/admin_suite/base_helper.rb +108 -108
  6. data/app/helpers/admin_suite/panels_helper.rb +1 -1
  7. data/app/javascript/controllers/admin_suite/file_upload_controller.js +9 -9
  8. data/app/javascript/controllers/admin_suite/json_editor_controller.js +8 -8
  9. data/app/javascript/controllers/admin_suite/searchable_select_controller.js +2 -2
  10. data/app/javascript/controllers/admin_suite/tag_select_controller.js +1 -1
  11. data/app/javascript/controllers/admin_suite/toggle_switch_controller.js +1 -1
  12. data/app/views/admin_suite/dashboard/index.html.erb +6 -15
  13. data/app/views/admin_suite/panels/_cards.html.erb +6 -6
  14. data/app/views/admin_suite/panels/_chart.html.erb +12 -12
  15. data/app/views/admin_suite/panels/_health.html.erb +14 -14
  16. data/app/views/admin_suite/panels/_recent.html.erb +11 -11
  17. data/app/views/admin_suite/panels/_stat.html.erb +24 -24
  18. data/app/views/admin_suite/panels/_table.html.erb +10 -10
  19. data/app/views/admin_suite/portals/show.html.erb +1 -1
  20. data/app/views/admin_suite/resources/_form.html.erb +1 -1
  21. data/app/views/admin_suite/resources/edit.html.erb +4 -4
  22. data/app/views/admin_suite/resources/index.html.erb +23 -23
  23. data/app/views/admin_suite/resources/new.html.erb +4 -4
  24. data/app/views/admin_suite/resources/show.html.erb +17 -17
  25. data/app/views/admin_suite/shared/_form.html.erb +8 -8
  26. data/app/views/admin_suite/shared/_json_editor_field.html.erb +4 -4
  27. data/app/views/admin_suite/shared/_sidebar.html.erb +4 -4
  28. data/app/views/admin_suite/shared/_topbar.html.erb +1 -1
  29. data/app/views/layouts/admin_suite/application.html.erb +4 -4
  30. data/docs/configuration.md +56 -6
  31. data/docs/portals.md +42 -0
  32. data/lib/admin/base/action_executor.rb +69 -0
  33. data/lib/admin_suite/configuration.rb +12 -0
  34. data/lib/admin_suite/engine.rb +82 -31
  35. data/lib/admin_suite/ui/field_renderer_registry.rb +2 -2
  36. data/lib/admin_suite/ui/form_field_renderer.rb +2 -2
  37. data/lib/admin_suite/ui/show_formatter_registry.rb +5 -5
  38. data/lib/admin_suite/ui/show_value_formatter.rb +1 -1
  39. data/lib/admin_suite/version.rb +1 -1
  40. data/lib/admin_suite.rb +31 -0
  41. data/lib/generators/admin_suite/install/templates/admin_suite.rb +8 -0
  42. data/test/dummy/log/test.log +1512 -0
  43. data/test/dummy/tmp/local_secret.txt +1 -0
  44. data/test/integration/dashboard_test.rb +57 -1
  45. data/test/lib/action_executor_test.rb +172 -0
  46. data/test/lib/zeitwerk_integration_test.rb +69 -16
  47. metadata +4 -1
@@ -52,19 +52,19 @@ export default class extends Controller {
52
52
  onDragOver(event) {
53
53
  event.preventDefault()
54
54
  event.stopPropagation()
55
- this.dropZoneElement.classList.add("border-amber-500", "bg-amber-50", "dark:bg-amber-900/10")
55
+ this.dropZoneElement.classList.add("border-amber-500", "bg-amber-50")
56
56
  }
57
57
 
58
58
  onDragLeave(event) {
59
59
  event.preventDefault()
60
60
  event.stopPropagation()
61
- this.dropZoneElement.classList.remove("border-amber-500", "bg-amber-50", "dark:bg-amber-900/10")
61
+ this.dropZoneElement.classList.remove("border-amber-500", "bg-amber-50")
62
62
  }
63
63
 
64
64
  onDrop(event) {
65
65
  event.preventDefault()
66
66
  event.stopPropagation()
67
- this.dropZoneElement.classList.remove("border-amber-500", "bg-amber-50", "dark:bg-amber-900/10")
67
+ this.dropZoneElement.classList.remove("border-amber-500", "bg-amber-50")
68
68
 
69
69
  const files = event.dataTransfer.files
70
70
  if (files.length > 0) {
@@ -141,8 +141,8 @@ export default class extends Controller {
141
141
  <svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
142
142
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
143
143
  </svg>
144
- <span class="font-medium text-slate-900 dark:text-white">${fileName}</span>
145
- <span class="text-slate-500 dark:text-slate-400">(${fileSize})</span>
144
+ <span class="font-medium text-slate-900">${fileName}</span>
145
+ <span class="text-slate-500">(${fileSize})</span>
146
146
  </div>
147
147
  `
148
148
  }
@@ -173,7 +173,7 @@ export default class extends Controller {
173
173
  if (!this.hasFilenameTarget) return
174
174
 
175
175
  this.filenameTarget.innerHTML = `
176
- <div class="flex items-center gap-2 text-red-600 dark:text-red-400">
176
+ <div class="flex items-center gap-2 text-red-600">
177
177
  <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
178
178
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
179
179
  </svg>
@@ -191,10 +191,10 @@ export default class extends Controller {
191
191
 
192
192
  this.progressTarget.classList.remove("hidden")
193
193
  this.progressTarget.innerHTML = `
194
- <div class="w-full bg-slate-200 dark:bg-slate-700 rounded-full h-2">
194
+ <div class="w-full bg-slate-200 rounded-full h-2">
195
195
  <div class="bg-amber-500 h-2 rounded-full transition-all duration-300" style="width: ${percent}%"></div>
196
196
  </div>
197
- <span class="text-xs text-slate-500 dark:text-slate-400">${percent}%</span>
197
+ <span class="text-xs text-slate-500">${percent}%</span>
198
198
  `
199
199
  }
200
200
 
@@ -211,7 +211,7 @@ export default class extends Controller {
211
211
 
212
212
  if (this.hasFilenameTarget) {
213
213
  this.filenameTarget.innerHTML = `
214
- <span class="text-slate-500 dark:text-slate-400">No file selected</span>
214
+ <span class="text-slate-500">No file selected</span>
215
215
  `
216
216
  }
217
217
 
@@ -17,12 +17,12 @@ export default class extends Controller {
17
17
  try {
18
18
  JSON.parse(value)
19
19
  this.clearError()
20
- this.inputTarget.classList.remove("border-red-500", "dark:border-red-500")
21
- this.inputTarget.classList.add("border-slate-300", "dark:border-slate-600")
20
+ this.inputTarget.classList.remove("border-red-500")
21
+ this.inputTarget.classList.add("border-slate-300")
22
22
  } catch (e) {
23
23
  this.showError(e.message)
24
- this.inputTarget.classList.remove("border-slate-300", "dark:border-slate-600")
25
- this.inputTarget.classList.add("border-red-500", "dark:border-red-500")
24
+ this.inputTarget.classList.remove("border-slate-300")
25
+ this.inputTarget.classList.add("border-red-500")
26
26
  }
27
27
  }
28
28
 
@@ -38,12 +38,12 @@ export default class extends Controller {
38
38
  const formatted = JSON.stringify(parsed, null, 2)
39
39
  this.inputTarget.value = formatted
40
40
  this.clearError()
41
- this.inputTarget.classList.remove("border-red-500", "dark:border-red-500")
42
- this.inputTarget.classList.add("border-slate-300", "dark:border-slate-600")
41
+ this.inputTarget.classList.remove("border-red-500")
42
+ this.inputTarget.classList.add("border-slate-300")
43
43
  } catch (e) {
44
44
  this.showError(e.message)
45
- this.inputTarget.classList.remove("border-slate-300", "dark:border-slate-600")
46
- this.inputTarget.classList.add("border-red-500", "dark:border-red-500")
45
+ this.inputTarget.classList.remove("border-slate-300")
46
+ this.inputTarget.classList.add("border-red-500")
47
47
  }
48
48
  }
49
49
 
@@ -105,7 +105,7 @@ export default class extends Controller {
105
105
  renderDropdown() {
106
106
  if (!this.filteredOptions.length) {
107
107
  this.dropdownTarget.innerHTML = `
108
- <div class="px-3 py-2 text-sm text-slate-400 dark:text-slate-500">No results found</div>
108
+ <div class="px-3 py-2 text-sm text-slate-400">No results found</div>
109
109
  `
110
110
  return
111
111
  }
@@ -114,7 +114,7 @@ export default class extends Controller {
114
114
  .map(
115
115
  (opt, index) => `
116
116
  <button type="button"
117
- class="block w-full text-left px-3 py-2 text-sm hover:bg-slate-100 dark:hover:bg-slate-700 ${index === this.selectedIndex ? "bg-slate-100 dark:bg-slate-700" : ""} ${opt.isNew ? "text-indigo-600 dark:text-indigo-400 font-medium" : "text-slate-700 dark:text-slate-200"}"
117
+ class="block w-full text-left px-3 py-2 text-sm hover:bg-slate-100 ${index === this.selectedIndex ? "bg-slate-100" : ""} ${opt.isNew ? "text-indigo-600 font-medium" : "text-slate-700"}"
118
118
  data-action="click->admin-suite--searchable-select#select"
119
119
  data-value="${opt.value}"
120
120
  data-label="${opt.label}">
@@ -118,7 +118,7 @@ export default class extends Controller {
118
118
 
119
119
  const tagEl = document.createElement("span")
120
120
  tagEl.className =
121
- "inline-flex items-center gap-1 px-2 py-1 bg-indigo-100 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300 rounded text-sm"
121
+ "inline-flex items-center gap-1 px-2 py-1 bg-indigo-100 text-indigo-700 rounded text-sm"
122
122
  tagEl.innerHTML = `
123
123
  ${this.escapeHtml(value)}
124
124
  <input type="hidden" name="${this.getFieldName()}" value="${this.escapeHtml(value)}">
@@ -20,7 +20,7 @@ export default class extends Controller {
20
20
  }
21
21
 
22
22
  get inactiveClasses() {
23
- return this.hasInactiveClassesValue ? this.inactiveClassesValue : "bg-slate-200 dark:bg-slate-700"
23
+ return this.hasInactiveClassesValue ? this.inactiveClassesValue : "bg-slate-200"
24
24
  }
25
25
 
26
26
  toggle(event) {
@@ -1,21 +1,12 @@
1
- <% content_for :title, "Developer Portal" %>
1
+ <% content_for :title, @page_title %>
2
2
 
3
3
  <div class="px-4 sm:px-6 lg:px-8 max-w-7xl mx-auto">
4
4
  <div class="mb-6">
5
- <h1 class="text-2xl font-bold text-slate-900 dark:text-white">Developer Portal</h1>
6
- <p class="text-sm text-slate-500 dark:text-slate-400 mt-1">
7
- Admin framework for managing all application resources across three specialized portals.
8
- </p>
9
- </div>
10
-
11
- <% @dashboard_sections.each do |section| %>
12
- <% if section[:title].present? %>
13
- <div class="mt-8 mb-3 flex items-center gap-2 text-sm font-semibold text-slate-900 dark:text-white">
14
- <%= admin_suite_icon("activity", class: "w-4 h-4 text-slate-400") %>
15
- <span><%= section[:title] %></span>
16
- </div>
5
+ <h1 class="text-2xl font-bold text-slate-900"><%= @page_title %></h1>
6
+ <% if @page_description.present? %>
7
+ <p class="text-sm text-slate-500 mt-1"><%= @page_description %></p>
17
8
  <% end %>
9
+ </div>
18
10
 
19
- <%= render_dashboard_rows(section[:rows]) %>
20
- <% end %>
11
+ <%= render_dashboard_rows(@dashboard_rows) %>
21
12
  </div>
@@ -1,14 +1,14 @@
1
1
  <% resources = Array(panel_eval(panel.options[:resources])) %>
2
2
  <% variant = (panel.options[:variant] || :default).to_sym %>
3
3
 
4
- <div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 overflow-hidden">
5
- <div class="px-4 py-3 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between">
6
- <h3 class="font-semibold text-slate-900 dark:text-white"><%= panel.title %></h3>
4
+ <div class="bg-white rounded-xl border border-slate-200 overflow-hidden">
5
+ <div class="px-4 py-3 border-b border-slate-200 flex items-center justify-between">
6
+ <h3 class="font-semibold text-slate-900"><%= panel.title %></h3>
7
7
  </div>
8
8
 
9
9
  <% if resources.any? %>
10
10
  <% if variant == :portals %>
11
- <div class="p-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
11
+ <div class="admin-suite-cards-grid admin-suite-cards-grid--portals p-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
12
12
  <% resources.each do |portal_item| %>
13
13
  <%
14
14
  key = portal_item[:key] || portal_item["key"]
@@ -52,7 +52,7 @@
52
52
  <% end %>
53
53
  </div>
54
54
  <% else %>
55
- <div class="p-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
55
+ <div class="admin-suite-cards-grid admin-suite-cards-grid--resources p-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
56
56
  <% resources.each do |resource_item| %>
57
57
  <%
58
58
  if resource_item.is_a?(Hash)
@@ -102,6 +102,6 @@
102
102
  </div>
103
103
  <% end %>
104
104
  <% else %>
105
- <div class="p-4 text-sm text-slate-500 dark:text-slate-400">No cards configured.</div>
105
+ <div class="p-4 text-sm text-slate-500">No cards configured.</div>
106
106
  <% end %>
107
107
  </div>
@@ -5,20 +5,20 @@
5
5
  <% max_value = 1.0 if max_value.zero? %>
6
6
 
7
7
  <% bar_color = case color
8
- when :amber then "bg-amber-500 dark:bg-amber-400"
9
- when :green then "bg-green-500 dark:bg-green-400"
10
- when :red then "bg-red-500 dark:bg-red-400"
11
- when :cyan then "bg-cyan-500 dark:bg-cyan-400"
12
- when :violet then "bg-violet-500 dark:bg-violet-400"
13
- when :indigo then "bg-indigo-500 dark:bg-indigo-400"
14
- else "bg-indigo-500 dark:bg-indigo-400"
8
+ when :amber then "bg-amber-500"
9
+ when :green then "bg-green-500"
10
+ when :red then "bg-red-500"
11
+ when :cyan then "bg-cyan-500"
12
+ when :violet then "bg-violet-500"
13
+ when :indigo then "bg-indigo-500"
14
+ else "bg-indigo-500"
15
15
  end %>
16
16
 
17
- <div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 p-4">
17
+ <div class="bg-white rounded-xl border border-slate-200 p-4">
18
18
  <div class="flex items-center justify-between mb-4">
19
- <h3 class="font-semibold text-slate-900 dark:text-white"><%= panel.title %></h3>
19
+ <h3 class="font-semibold text-slate-900"><%= panel.title %></h3>
20
20
  <% if total.present? %>
21
- <span class="text-2xl font-bold text-slate-900 dark:text-white"><%= total %></span>
21
+ <span class="text-2xl font-bold text-slate-900"><%= total %></span>
22
22
  <% end %>
23
23
  </div>
24
24
 
@@ -38,10 +38,10 @@ end %>
38
38
 
39
39
  <div class="flex gap-1 mt-2">
40
40
  <% data.each do |d| %>
41
- <div class="flex-1 text-center text-xs text-slate-400 dark:text-slate-500"><%= d[:label].to_s.first(3) %></div>
41
+ <div class="flex-1 text-center text-xs text-slate-400"><%= d[:label].to_s.first(3) %></div>
42
42
  <% end %>
43
43
  </div>
44
44
  <% else %>
45
- <div class="p-4 text-sm text-slate-500 dark:text-slate-400">No chart data.</div>
45
+ <div class="p-4 text-sm text-slate-500">No chart data.</div>
46
46
  <% end %>
47
47
  </div>
@@ -3,26 +3,26 @@
3
3
 
4
4
  <% status_config = case status
5
5
  when :healthy
6
- { border: "border-green-200 dark:border-green-800/50",
7
- badge: "bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400",
6
+ { border: "border-green-200",
7
+ badge: "bg-green-100 text-green-700",
8
8
  dot: "bg-green-500 animate-pulse" }
9
9
  when :degraded
10
- { border: "border-amber-200 dark:border-amber-800/50",
11
- badge: "bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400",
10
+ { border: "border-amber-200",
11
+ badge: "bg-amber-100 text-amber-700",
12
12
  dot: "bg-amber-500 animate-pulse" }
13
13
  when :critical
14
- { border: "border-red-200 dark:border-red-800/50",
15
- badge: "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400",
14
+ { border: "border-red-200",
15
+ badge: "bg-red-100 text-red-700",
16
16
  dot: "bg-red-500 animate-pulse" }
17
17
  else
18
- { border: "border-slate-200 dark:border-slate-700",
19
- badge: "bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-400",
18
+ { border: "border-slate-200",
19
+ badge: "bg-slate-100 text-slate-600",
20
20
  dot: "bg-slate-400" }
21
21
  end %>
22
22
 
23
- <div class="bg-white dark:bg-slate-800 rounded-xl border <%= status_config[:border] %> overflow-hidden">
24
- <div class="px-4 py-3 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between">
25
- <h3 class="font-semibold text-slate-900 dark:text-white"><%= panel.title %></h3>
23
+ <div class="bg-white rounded-xl border <%= status_config[:border] %> overflow-hidden">
24
+ <div class="px-4 py-3 border-b border-slate-200 flex items-center justify-between">
25
+ <h3 class="font-semibold text-slate-900"><%= panel.title %></h3>
26
26
  <span class="flex items-center gap-1.5 px-2 py-1 rounded-full text-xs font-medium <%= status_config[:badge] %>">
27
27
  <span class="w-2 h-2 rounded-full <%= status_config[:dot] %>"></span>
28
28
  <%= status.to_s.humanize %>
@@ -33,12 +33,12 @@ end %>
33
33
  <div class="p-4 grid grid-cols-2 gap-3">
34
34
  <% metrics.each do |key, val| %>
35
35
  <div>
36
- <div class="text-lg font-semibold text-slate-900 dark:text-white"><%= val %></div>
37
- <div class="text-xs text-slate-500 dark:text-slate-400"><%= key.to_s.humanize %></div>
36
+ <div class="text-lg font-semibold text-slate-900"><%= val %></div>
37
+ <div class="text-xs text-slate-500"><%= key.to_s.humanize %></div>
38
38
  </div>
39
39
  <% end %>
40
40
  </div>
41
41
  <% else %>
42
- <div class="p-4 text-sm text-slate-500 dark:text-slate-400">No metrics.</div>
42
+ <div class="p-4 text-sm text-slate-500">No metrics.</div>
43
43
  <% end %>
44
44
  </div>
@@ -6,16 +6,16 @@
6
6
  <% view_all_path = panel_eval(panel.options[:view_all_path]) %>
7
7
  <% link_proc = panel.options[:link] %>
8
8
 
9
- <div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 overflow-hidden">
10
- <div class="px-4 py-3 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between">
11
- <h3 class="font-semibold text-slate-900 dark:text-white"><%= panel.title %></h3>
9
+ <div class="bg-white rounded-xl border border-slate-200 overflow-hidden">
10
+ <div class="px-4 py-3 border-b border-slate-200 flex items-center justify-between">
11
+ <h3 class="font-semibold text-slate-900"><%= panel.title %></h3>
12
12
  <% if view_all_path.present? %>
13
13
  <%= link_to "View all →", view_all_path, class: "text-sm #{theme_link_class}" %>
14
14
  <% end %>
15
15
  </div>
16
16
 
17
17
  <% if items.any? %>
18
- <ul class="divide-y divide-slate-100 dark:divide-slate-700">
18
+ <ul class="divide-y divide-slate-100">
19
19
  <% items.each do |item| %>
20
20
  <% path =
21
21
  if link_proc.respond_to?(:call)
@@ -24,25 +24,25 @@
24
24
  auto_admin_suite_path_for(item)
25
25
  end
26
26
  %>
27
- <li class="px-4 py-3 hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors">
27
+ <li class="px-4 py-3 hover:bg-slate-50 transition-colors">
28
28
  <% title = item_display_title(item) rescue item.to_s %>
29
29
  <% subtitle = (item.respond_to?(:created_at) && item.created_at) ? "#{time_ago_in_words(item.created_at)} ago" : nil %>
30
30
  <% if path.present? %>
31
31
  <%= link_to path, class: "flex items-center justify-between gap-3" do %>
32
32
  <div class="min-w-0">
33
- <div class="text-sm font-medium text-slate-900 dark:text-white truncate"><%= title.to_s.truncate(48) %></div>
33
+ <div class="text-sm font-medium text-slate-900 truncate"><%= title.to_s.truncate(48) %></div>
34
34
  <% if subtitle.present? %>
35
- <div class="text-xs text-slate-500 dark:text-slate-400"><%= subtitle %></div>
35
+ <div class="text-xs text-slate-500"><%= subtitle %></div>
36
36
  <% end %>
37
37
  </div>
38
- <%= admin_suite_icon("chevron-right", class: "w-4 h-4 text-slate-300 dark:text-slate-600 flex-shrink-0") %>
38
+ <%= admin_suite_icon("chevron-right", class: "w-4 h-4 text-slate-300 flex-shrink-0") %>
39
39
  <% end %>
40
40
  <% else %>
41
41
  <div class="flex items-center justify-between gap-3">
42
42
  <div class="min-w-0">
43
- <div class="text-sm font-medium text-slate-900 dark:text-white truncate"><%= title.to_s.truncate(48) %></div>
43
+ <div class="text-sm font-medium text-slate-900 truncate"><%= title.to_s.truncate(48) %></div>
44
44
  <% if subtitle.present? %>
45
- <div class="text-xs text-slate-500 dark:text-slate-400"><%= subtitle %></div>
45
+ <div class="text-xs text-slate-500"><%= subtitle %></div>
46
46
  <% end %>
47
47
  </div>
48
48
  </div>
@@ -51,6 +51,6 @@
51
51
  <% end %>
52
52
  </ul>
53
53
  <% else %>
54
- <div class="p-4 text-center text-sm text-slate-500 dark:text-slate-400"><%= empty_message %></div>
54
+ <div class="p-4 text-center text-sm text-slate-500"><%= empty_message %></div>
55
55
  <% end %>
56
56
  </div>
@@ -7,35 +7,35 @@
7
7
 
8
8
  <% color_classes = case color
9
9
  when :green
10
- { bg: "bg-gradient-to-br from-green-50 to-green-100 dark:from-green-900/20 dark:to-green-800/20",
11
- border: "border border-green-200 dark:border-green-800/50",
12
- text: "text-green-700 dark:text-green-400",
13
- label: "text-green-600 dark:text-green-500" }
10
+ { bg: "bg-gradient-to-br from-green-50 to-green-100",
11
+ border: "border border-green-200",
12
+ text: "text-green-700",
13
+ label: "text-green-600" }
14
14
  when :red
15
- { bg: "bg-gradient-to-br from-red-50 to-red-100 dark:from-red-900/20 dark:to-red-800/20",
16
- border: "border border-red-200 dark:border-red-800/50",
17
- text: "text-red-700 dark:text-red-400",
18
- label: "text-red-600 dark:text-red-500" }
15
+ { bg: "bg-gradient-to-br from-red-50 to-red-100",
16
+ border: "border border-red-200",
17
+ text: "text-red-700",
18
+ label: "text-red-600" }
19
19
  when :amber
20
- { bg: "bg-gradient-to-br from-amber-50 to-amber-100 dark:from-amber-900/20 dark:to-amber-800/20",
21
- border: "border border-amber-200 dark:border-amber-800/50",
22
- text: "text-amber-700 dark:text-amber-400",
23
- label: "text-amber-600 dark:text-amber-500" }
20
+ { bg: "bg-gradient-to-br from-amber-50 to-amber-100",
21
+ border: "border border-amber-200",
22
+ text: "text-amber-700",
23
+ label: "text-amber-600" }
24
24
  when :cyan
25
- { bg: "bg-gradient-to-br from-cyan-50 to-cyan-100 dark:from-cyan-900/20 dark:to-cyan-800/20",
26
- border: "border border-cyan-200 dark:border-cyan-800/50",
27
- text: "text-cyan-700 dark:text-cyan-400",
28
- label: "text-cyan-600 dark:text-cyan-500" }
25
+ { bg: "bg-gradient-to-br from-cyan-50 to-cyan-100",
26
+ border: "border border-cyan-200",
27
+ text: "text-cyan-700",
28
+ label: "text-cyan-600" }
29
29
  when :violet
30
- { bg: "bg-gradient-to-br from-violet-50 to-violet-100 dark:from-violet-900/20 dark:to-violet-800/20",
31
- border: "border border-violet-200 dark:border-violet-800/50",
32
- text: "text-violet-700 dark:text-violet-400",
33
- label: "text-violet-600 dark:text-violet-500" }
30
+ { bg: "bg-gradient-to-br from-violet-50 to-violet-100",
31
+ border: "border border-violet-200",
32
+ text: "text-violet-700",
33
+ label: "text-violet-600" }
34
34
  else
35
- { bg: "bg-white dark:bg-slate-800",
36
- border: "border border-slate-200 dark:border-slate-700",
37
- text: "text-slate-900 dark:text-white",
38
- label: "text-slate-500 dark:text-slate-400" }
35
+ { bg: "bg-white",
36
+ border: "border border-slate-200",
37
+ text: "text-slate-900",
38
+ label: "text-slate-500" }
39
39
  end %>
40
40
 
41
41
  <% if variant == :mini %>
@@ -2,28 +2,28 @@
2
2
  <% rows = Array(rows_value) %>
3
3
  <% columns = Array(panel.options[:columns]).presence %>
4
4
 
5
- <div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 overflow-hidden">
6
- <div class="px-4 py-3 border-b border-slate-200 dark:border-slate-700 flex items-center justify-between">
7
- <h3 class="font-semibold text-slate-900 dark:text-white"><%= panel.title %></h3>
5
+ <div class="bg-white rounded-xl border border-slate-200 overflow-hidden">
6
+ <div class="px-4 py-3 border-b border-slate-200 flex items-center justify-between">
7
+ <h3 class="font-semibold text-slate-900"><%= panel.title %></h3>
8
8
  </div>
9
9
 
10
10
  <% if rows.any? %>
11
11
  <% columns ||= rows.first.respond_to?(:attributes) ? rows.first.attributes.keys.first(6).map(&:to_sym) : [] %>
12
12
  <div class="overflow-x-auto">
13
- <table class="min-w-full divide-y divide-slate-200 dark:divide-slate-700">
14
- <thead class="bg-slate-50/50 dark:bg-slate-900/30">
13
+ <table class="min-w-full divide-y divide-slate-200">
14
+ <thead class="bg-slate-50/50">
15
15
  <tr>
16
16
  <% columns.each do |col| %>
17
- <th class="px-4 py-2.5 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider"><%= col.to_s.humanize %></th>
17
+ <th class="px-4 py-2.5 text-left text-xs font-medium text-slate-500 uppercase tracking-wider"><%= col.to_s.humanize %></th>
18
18
  <% end %>
19
19
  </tr>
20
20
  </thead>
21
- <tbody class="divide-y divide-slate-200 dark:divide-slate-700">
21
+ <tbody class="divide-y divide-slate-200">
22
22
  <% rows.each do |row| %>
23
- <tr class="hover:bg-slate-50/50 dark:hover:bg-slate-900/30">
23
+ <tr class="hover:bg-slate-50/50">
24
24
  <% columns.each do |col| %>
25
25
  <% val = row.respond_to?(col) ? (row.public_send(col) rescue nil) : (row[col] rescue nil) %>
26
- <td class="px-4 py-3 text-sm text-slate-700 dark:text-slate-200"><%= format_table_cell(val) rescue val.to_s %></td>
26
+ <td class="px-4 py-3 text-sm text-slate-700"><%= format_table_cell(val) rescue val.to_s %></td>
27
27
  <% end %>
28
28
  </tr>
29
29
  <% end %>
@@ -31,6 +31,6 @@
31
31
  </table>
32
32
  </div>
33
33
  <% else %>
34
- <div class="p-4 text-sm text-slate-500 dark:text-slate-400">No rows.</div>
34
+ <div class="p-4 text-sm text-slate-500">No rows.</div>
35
35
  <% end %>
36
36
  </div>
@@ -35,7 +35,7 @@
35
35
  <%= render_dashboard_rows(@dashboard_rows) %>
36
36
  <% else %>
37
37
  <!-- Sections (fallback) -->
38
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
38
+ <div class="admin-suite-portal-sections-grid grid grid-cols-1 lg:grid-cols-2 gap-6">
39
39
  <% @sections.each do |_section_key, section| %>
40
40
  <div class="bg-white rounded-xl border border-slate-200 overflow-hidden">
41
41
  <div class="px-6 py-4 border-b border-slate-200 bg-slate-50 flex items-center justify-between">
@@ -17,7 +17,7 @@
17
17
  <% columns = resource.class.column_names.reject { |c| %w[id created_at updated_at].include?(c) } %>
18
18
  <% columns.each do |column| %>
19
19
  <div>
20
- <%= f.label column, class: "block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1" %>
20
+ <%= f.label column, class: "block text-sm font-medium text-slate-700 mb-1" %>
21
21
  <%= f.text_field column, class: "form-input w-full" %>
22
22
  </div>
23
23
  <% end %>
@@ -3,20 +3,20 @@
3
3
  <div class="px-4 sm:px-6 lg:px-8 max-w-3xl mx-auto">
4
4
  <!-- Header -->
5
5
  <div class="mb-6">
6
- <nav class="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400 mb-2">
6
+ <nav class="flex items-center gap-2 text-sm text-slate-500 mb-2">
7
7
  <%= link_to "Dashboard", root_path, class: theme_link_hover_text_class %>
8
8
  <%= admin_suite_icon("chevron-right", class: "w-4 h-4") %>
9
9
  <%= link_to resource_config.human_name_plural, url_for(action: :index), class: theme_link_hover_text_class %>
10
10
  <%= admin_suite_icon("chevron-right", class: "w-4 h-4") %>
11
11
  <%= link_to @resource.respond_to?(:name) ? @resource.name.truncate(20) : "##{@resource.id}", url_for(action: :show, id: @resource.id), class: theme_link_hover_text_class %>
12
12
  <%= admin_suite_icon("chevron-right", class: "w-4 h-4") %>
13
- <span class="text-slate-900 dark:text-white">Edit</span>
13
+ <span class="text-slate-900">Edit</span>
14
14
  </nav>
15
- <h1 class="text-2xl font-bold text-slate-900 dark:text-white">Edit <%= resource_config.human_name %></h1>
15
+ <h1 class="text-2xl font-bold text-slate-900">Edit <%= resource_config.human_name %></h1>
16
16
  </div>
17
17
 
18
18
  <!-- Form -->
19
- <div class="bg-white dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700 overflow-hidden">
19
+ <div class="bg-white rounded-xl border border-slate-200 overflow-hidden">
20
20
  <div class="p-6">
21
21
  <%= render "admin_suite/shared/form", resource: @resource %>
22
22
  </div>