admin_suite 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +17 -0
- data/README.md +137 -1
- data/docs/README.md +26 -0
- data/docs/actions.md +98 -0
- data/docs/configuration.md +198 -0
- data/docs/development.md +64 -0
- data/docs/docs_viewer.md +79 -0
- data/docs/fields.md +188 -0
- data/docs/installation.md +80 -0
- data/docs/portals.md +98 -0
- data/docs/releasing.md +38 -0
- data/docs/resources.md +237 -0
- data/docs/theming.md +63 -0
- data/docs/troubleshooting.md +50 -0
- data/lib/admin_suite/version.rb +1 -1
- data/test/dummy/log/test.log +109 -403
- data/test/dummy/tmp/local_secret.txt +1 -1
- metadata +18 -3
data/docs/fields.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Fields
|
|
2
|
+
|
|
3
|
+
Fields are defined in the resource `form do ... end` block:
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
form do
|
|
7
|
+
field :name
|
|
8
|
+
field :status, type: :select, collection: [["Active", "active"], ["Inactive", "inactive"]]
|
|
9
|
+
end
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Common options
|
|
13
|
+
|
|
14
|
+
All fields support:
|
|
15
|
+
|
|
16
|
+
- `type:` (defaults to `:text`)
|
|
17
|
+
- `required:` (`true/false`)
|
|
18
|
+
- `label:` (String)
|
|
19
|
+
- `help:` (String)
|
|
20
|
+
- `placeholder:` (String)
|
|
21
|
+
- `readonly:` (`true/false`)
|
|
22
|
+
- `if:` Proc (render only if truthy)
|
|
23
|
+
- `unless:` Proc (render only if falsy)
|
|
24
|
+
|
|
25
|
+
Example conditional field:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
field :admin_notes, type: :textarea, if: ->(record) { record.admin? }
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Supported field types
|
|
32
|
+
|
|
33
|
+
### Text-like
|
|
34
|
+
|
|
35
|
+
- `:text` (default)
|
|
36
|
+
- `:textarea` (`rows:` supported)
|
|
37
|
+
- `:email`
|
|
38
|
+
- `:url`
|
|
39
|
+
- `:number`
|
|
40
|
+
- `:date`
|
|
41
|
+
- `:time`
|
|
42
|
+
- `:datetime`
|
|
43
|
+
|
|
44
|
+
### Toggle
|
|
45
|
+
|
|
46
|
+
- `:toggle` (renders a switch)
|
|
47
|
+
|
|
48
|
+
```ruby
|
|
49
|
+
field :enabled, type: :toggle
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Select
|
|
53
|
+
|
|
54
|
+
- `:select` (uses Rails `select`)
|
|
55
|
+
|
|
56
|
+
Options:
|
|
57
|
+
|
|
58
|
+
- `collection:` Array of `[label, value]` or simple values
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
field :status, type: :select, collection: [["Active", "active"], ["Inactive", "inactive"]]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Searchable select
|
|
65
|
+
|
|
66
|
+
- `:searchable_select` (Stimulus-powered searchable dropdown)
|
|
67
|
+
|
|
68
|
+
Options:
|
|
69
|
+
|
|
70
|
+
- `collection:` either:
|
|
71
|
+
- an Array (static options), or
|
|
72
|
+
- a String URL (advanced; used by the JS controller as a “search URL”)
|
|
73
|
+
- `create_url:` (String) enables “creatable” behavior in the UI
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
field :company_id,
|
|
77
|
+
type: :searchable_select,
|
|
78
|
+
collection: Company.order(:name).pluck(:name, :id),
|
|
79
|
+
placeholder: "Search companies..."
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Multi-select & tags
|
|
83
|
+
|
|
84
|
+
- `:multi_select`
|
|
85
|
+
- `:tags`
|
|
86
|
+
|
|
87
|
+
Options:
|
|
88
|
+
|
|
89
|
+
- `collection:` Array of options (used for suggestions)
|
|
90
|
+
- `create_url:` enables “creatable” behavior
|
|
91
|
+
- `multiple:` boolean (reserved; arrays are permitted automatically)
|
|
92
|
+
|
|
93
|
+
Notes:
|
|
94
|
+
|
|
95
|
+
- These submit arrays and are permitted automatically by AdminSuite.
|
|
96
|
+
- For `:tags`, AdminSuite uses a `tag_list` parameter by default (or `#{field_name}_list` if your model exposes it).
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
field :tag_list, type: :tags, placeholder: "Add tags..."
|
|
100
|
+
field :roles, type: :multi_select, collection: %w[admin editor viewer]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### File uploads / attachments
|
|
104
|
+
|
|
105
|
+
- `:file`
|
|
106
|
+
- `:attachment`
|
|
107
|
+
- `:image`
|
|
108
|
+
|
|
109
|
+
Options:
|
|
110
|
+
|
|
111
|
+
- `accept:` MIME accept string (e.g. `"image/*"`, `"application/pdf"`)
|
|
112
|
+
|
|
113
|
+
These assume your host app uses **Active Storage**.
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
field :avatar, type: :image, accept: "image/*"
|
|
117
|
+
field :resume, type: :file, accept: "application/pdf"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Rich text
|
|
121
|
+
|
|
122
|
+
- `:trix`
|
|
123
|
+
- `:rich_text`
|
|
124
|
+
|
|
125
|
+
These assume your host app uses **Action Text**.
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
field :bio, type: :rich_text
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Markdown
|
|
132
|
+
|
|
133
|
+
- `:markdown` (textarea enhanced by EasyMDE via CDN in the engine layout)
|
|
134
|
+
|
|
135
|
+
```ruby
|
|
136
|
+
field :prompt_template, type: :markdown, rows: 16
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### JSON editor
|
|
140
|
+
|
|
141
|
+
- `:json` (renders the engine’s JSON editor partial)
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
field :settings, type: :json
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Code editor
|
|
148
|
+
|
|
149
|
+
- `:code` (monospace editor container; enhanced by engine JS)
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
field :ruby_code, type: :code, rows: 20
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Label (read-only)
|
|
156
|
+
|
|
157
|
+
- `:label` renders a badge-like value (useful for status fields)
|
|
158
|
+
|
|
159
|
+
Options:
|
|
160
|
+
|
|
161
|
+
- `label_color:` Symbol or Proc
|
|
162
|
+
- `label_size:` `:sm`/`:md` or Proc
|
|
163
|
+
|
|
164
|
+
```ruby
|
|
165
|
+
field :status, type: :label, label_color: ->(r) { r.active? ? :emerald : :slate }, label_size: :sm
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Layout helpers
|
|
169
|
+
|
|
170
|
+
Inside `form do ... end` you can group fields:
|
|
171
|
+
|
|
172
|
+
### `section`
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
section "Billing", description: "Payment settings", collapsible: true do
|
|
176
|
+
field :stripe_customer_id, readonly: true
|
|
177
|
+
end
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### `row`
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
row cols: 2 do
|
|
184
|
+
field :first_name
|
|
185
|
+
field :last_name
|
|
186
|
+
end
|
|
187
|
+
```
|
|
188
|
+
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Installation
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
- Ruby **3.2+**
|
|
6
|
+
- Rails **8.0+**
|
|
7
|
+
|
|
8
|
+
AdminSuite is a mountable engine. You mount it under a path in your host app’s `config/routes.rb`.
|
|
9
|
+
|
|
10
|
+
## Add the gem
|
|
11
|
+
|
|
12
|
+
In your host app `Gemfile`:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
gem "admin_suite"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bundle install
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Install into your app
|
|
25
|
+
|
|
26
|
+
Run the install generator (creates an initializer and mounts the engine):
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bin/rails g admin_suite:install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
To mount at a custom path:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bin/rails g admin_suite:install --mount-path=/internal/admin
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This will:
|
|
39
|
+
|
|
40
|
+
- Create `config/initializers/admin_suite.rb`
|
|
41
|
+
- Add a route like `mount AdminSuite::Engine => "/internal/admin"`
|
|
42
|
+
|
|
43
|
+
## First run checklist
|
|
44
|
+
|
|
45
|
+
- **Auth**: set `config.authenticate` so only permitted users can access AdminSuite.
|
|
46
|
+
- **Actor**: set `config.current_actor` if you want actions/auditing to know “who did it”.
|
|
47
|
+
- **Resources**: add at least one resource definition file (see [Resources](resources.md)).
|
|
48
|
+
- **Portals**: set up portal metadata and dashboards (see [Portals](portals.md)).
|
|
49
|
+
|
|
50
|
+
## Assets (CSS/JS)
|
|
51
|
+
|
|
52
|
+
AdminSuite ships with:
|
|
53
|
+
|
|
54
|
+
- A small baseline stylesheet (`admin_suite.css`)
|
|
55
|
+
- A compiled Tailwind stylesheet (`admin_suite_tailwind.css`) that is built into the host app at asset precompile time
|
|
56
|
+
- Engine-provided Stimulus controllers via importmap
|
|
57
|
+
|
|
58
|
+
### CSS build behavior
|
|
59
|
+
|
|
60
|
+
When your app runs `assets:precompile`, AdminSuite automatically runs:
|
|
61
|
+
|
|
62
|
+
- `admin_suite:tailwind:build` → writes `app/assets/builds/admin_suite_tailwind.css` in your **host app**
|
|
63
|
+
|
|
64
|
+
So in production you typically just need to ensure your deployment runs `assets:precompile` as usual.
|
|
65
|
+
|
|
66
|
+
### Host stylesheet overrides (optional)
|
|
67
|
+
|
|
68
|
+
If your app already uses Tailwind (or you want custom branding), you can include your app stylesheet after AdminSuite:
|
|
69
|
+
|
|
70
|
+
- Set `config.host_stylesheet` (see [Theming & assets](theming.md))
|
|
71
|
+
|
|
72
|
+
## Routes and URLs
|
|
73
|
+
|
|
74
|
+
Assuming you mounted at `/internal/admin`:
|
|
75
|
+
|
|
76
|
+
- `/internal/admin` → AdminSuite dashboard
|
|
77
|
+
- `/internal/admin/docs` → docs viewer (optional)
|
|
78
|
+
- `/internal/admin/:portal` → portal dashboard (optional)
|
|
79
|
+
- `/internal/admin/:portal/:resource_name` → resource index
|
|
80
|
+
|
data/docs/portals.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Portals & dashboards
|
|
2
|
+
|
|
3
|
+
AdminSuite navigation is organized by **portal** and **section**:
|
|
4
|
+
|
|
5
|
+
- A **portal** is the top-level grouping (e.g. `:ops`, `:ai`)
|
|
6
|
+
- A **section** is a grouping within a portal (e.g. `:billing`, `:users`)
|
|
7
|
+
- A **resource** belongs to exactly one portal + section via the resource DSL
|
|
8
|
+
|
|
9
|
+
You can configure portals in two complementary ways:
|
|
10
|
+
|
|
11
|
+
1. **Portal metadata** via `AdminSuite.config.portals` (label/icon/color/order)
|
|
12
|
+
2. **Portal DSL** via `AdminSuite.portal(:key) { ... }` (metadata + dashboard layout)
|
|
13
|
+
|
|
14
|
+
## Portal metadata (`config.portals`)
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
AdminSuite.configure do |config|
|
|
18
|
+
config.portals = {
|
|
19
|
+
ops: { label: "Ops", icon: "settings", color: :amber, order: 10 },
|
|
20
|
+
ai: { label: "AI", icon: "cpu", color: :cyan, order: 20 }
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Portal fields
|
|
26
|
+
|
|
27
|
+
- `label` (String): display label
|
|
28
|
+
- `icon` (String/Symbol): lucide icon name (e.g. `"settings"`)
|
|
29
|
+
- `color` (Symbol/String): used for accents (`:amber`, `:emerald`, `:cyan`, `:violet`, `:slate`)
|
|
30
|
+
- `order` (Integer): sort order in the root dashboard and sidebar
|
|
31
|
+
- `description` (String, optional): shown on the root dashboard cards when present
|
|
32
|
+
|
|
33
|
+
## Portal DSL (`AdminSuite.portal`)
|
|
34
|
+
|
|
35
|
+
Portal DSL files are loaded from `AdminSuite.config.portal_globs` (defaults include
|
|
36
|
+
`config/admin_suite/portals/*.rb`, `app/admin/portals/*.rb`, `app/admin_suite/portals/*.rb`).
|
|
37
|
+
|
|
38
|
+
Example file:
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
# config/admin_suite/portals/ops.rb
|
|
42
|
+
AdminSuite.portal :ops do
|
|
43
|
+
label "Ops Portal"
|
|
44
|
+
icon "settings"
|
|
45
|
+
color :amber
|
|
46
|
+
order 10
|
|
47
|
+
description "Operational tools and internal resources."
|
|
48
|
+
|
|
49
|
+
dashboard do
|
|
50
|
+
row do
|
|
51
|
+
stat_panel "New users (24h)", -> { User.where("created_at > ?", 24.hours.ago).count }, color: :emerald, span: 3
|
|
52
|
+
stat_panel "Failed jobs", -> { SolidQueue::FailedExecution.count }, color: :red, span: 3
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
row do
|
|
56
|
+
recent_panel "Recent signups", scope: -> { User.order(created_at: :desc).limit(5) }, span: 6
|
|
57
|
+
table_panel "Queue summary",
|
|
58
|
+
rows: -> { SolidQueue::Job.order(created_at: :desc).limit(10) },
|
|
59
|
+
columns: %i[id class_name created_at],
|
|
60
|
+
span: 6
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Dashboard DSL
|
|
67
|
+
|
|
68
|
+
The dashboard DSL is available inside `portal.dashboard do ... end`.
|
|
69
|
+
|
|
70
|
+
- `dashboard` contains `row { ... }`
|
|
71
|
+
- each `row` contains one or more `panel(...)` calls
|
|
72
|
+
|
|
73
|
+
### Panel helpers
|
|
74
|
+
|
|
75
|
+
These are convenience helpers that all create a `panel` under the hood:
|
|
76
|
+
|
|
77
|
+
- `stat_panel(title, value=nil, span: nil, **options, &block)`
|
|
78
|
+
- `health_panel(title, status: nil, metrics: nil, span: nil, **options, &block)`
|
|
79
|
+
- `chart_panel(title, data: nil, span: nil, **options, &block)`
|
|
80
|
+
- `cards_panel(title, resources: nil, span: nil, **options, &block)`
|
|
81
|
+
- `recent_panel(title, scope: nil, link: nil, span: nil, **options, &block)`
|
|
82
|
+
- `table_panel(title, rows: nil, columns: nil, span: nil, **options, &block)`
|
|
83
|
+
|
|
84
|
+
### `span`
|
|
85
|
+
|
|
86
|
+
`span` controls width in a 12-column grid. Typical values: `3`, `4`, `6`, `12`.
|
|
87
|
+
|
|
88
|
+
## Portal pages
|
|
89
|
+
|
|
90
|
+
Once mounted, portal pages are served at:
|
|
91
|
+
|
|
92
|
+
- `/:portal` (relative to the mount path)
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
|
|
96
|
+
- `/internal/admin/ops`
|
|
97
|
+
- `/internal/admin/ai`
|
|
98
|
+
|
data/docs/releasing.md
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
This page is intended for maintainers publishing `admin_suite` to RubyGems.
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
1. Bump the version
|
|
8
|
+
|
|
9
|
+
- Update `lib/admin_suite/version.rb`
|
|
10
|
+
|
|
11
|
+
2. Update changelog
|
|
12
|
+
|
|
13
|
+
- Add an entry to `CHANGELOG.md`
|
|
14
|
+
|
|
15
|
+
3. Run tests and build the gem
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bundle exec rake test
|
|
19
|
+
gem build admin_suite.gemspec
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
4. (Recommended) Tag the release
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git tag -a "vX.Y.Z" -m "AdminSuite vX.Y.Z"
|
|
26
|
+
git push --tags
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
5. Publish to RubyGems
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
gem push "admin_suite-X.Y.Z.gem"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
36
|
+
|
|
37
|
+
- RubyGems commonly requires MFA/OTP for pushes (this gem is configured with `rubygems_mfa_required`).
|
|
38
|
+
|
data/docs/resources.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Resources
|
|
2
|
+
|
|
3
|
+
Resources are defined using `Admin::Base::Resource`.
|
|
4
|
+
|
|
5
|
+
AdminSuite loads resource definition files from `AdminSuite.config.resource_globs`
|
|
6
|
+
(defaults include `config/admin_suite/resources/*.rb` and `app/admin/resources/*.rb`).
|
|
7
|
+
|
|
8
|
+
## Create a resource
|
|
9
|
+
|
|
10
|
+
A resource is a Ruby class under `Admin::Resources` ending in `Resource`.
|
|
11
|
+
|
|
12
|
+
Example:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
# config/admin_suite/resources/user.rb
|
|
16
|
+
module Admin
|
|
17
|
+
module Resources
|
|
18
|
+
class UserResource < Admin::Base::Resource
|
|
19
|
+
model ::User
|
|
20
|
+
portal :ops
|
|
21
|
+
section :accounts
|
|
22
|
+
|
|
23
|
+
nav label: "Users", icon: "users", order: 10
|
|
24
|
+
|
|
25
|
+
index do
|
|
26
|
+
searchable :email, :name
|
|
27
|
+
sortable :created_at, :email, default: :created_at, direction: :desc
|
|
28
|
+
paginate 50
|
|
29
|
+
|
|
30
|
+
columns do
|
|
31
|
+
column :id
|
|
32
|
+
column :email
|
|
33
|
+
column :created_at
|
|
34
|
+
column :admin, type: :toggle, toggle_field: :admin, header: "Admin?"
|
|
35
|
+
column :status, type: :label, label_color: ->(u) { u.active? ? :emerald : :slate }, label_size: :sm
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
filters do
|
|
39
|
+
filter :search, type: :text, placeholder: "Search users..."
|
|
40
|
+
filter :status, type: :select, options: [["Active", "active"], ["Inactive", "inactive"]]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
stats do
|
|
44
|
+
stat :total, -> { User.count }, color: :slate
|
|
45
|
+
stat :new_24h, -> { User.where("created_at > ?", 24.hours.ago).count }, color: :emerald
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
form do
|
|
50
|
+
section "Basics", description: "Core account fields" do
|
|
51
|
+
field :email, type: :email, required: true
|
|
52
|
+
field :name, required: true
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
row cols: 2 do
|
|
56
|
+
field :admin, type: :toggle, help: "Grants access to internal tools."
|
|
57
|
+
field :status, type: :select, collection: [["Active", "active"], ["Inactive", "inactive"]]
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
show do
|
|
62
|
+
main do
|
|
63
|
+
panel :details, title: "User details", fields: %i[email name status created_at]
|
|
64
|
+
panel :activity, title: "Activity", render: :custom_activity_timeline
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
sidebar do
|
|
68
|
+
panel :summary, title: "Summary", fields: %i[id admin]
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
actions do
|
|
73
|
+
action :reset_password, label: "Reset password", icon: "key", confirm: "Send reset email?"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Core DSL
|
|
81
|
+
|
|
82
|
+
### Model
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
model ::User
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Navigation placement
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
portal :ops
|
|
92
|
+
section :accounts
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
These determine:
|
|
96
|
+
|
|
97
|
+
- URL: `/:portal/:resource_name`
|
|
98
|
+
- Sidebar placement: portal group → section group → resource link
|
|
99
|
+
|
|
100
|
+
### Navigation metadata
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
nav label: "Users", icon: "users", order: 10
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Also available as convenience setters:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
label "Users"
|
|
110
|
+
icon "users"
|
|
111
|
+
order 10
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Index DSL (`index do ... end`)
|
|
115
|
+
|
|
116
|
+
### Search
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
searchable :name, :email
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Search uses `ILIKE` across the configured fields.
|
|
123
|
+
|
|
124
|
+
### Sort
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
sortable :created_at, :email, default: :created_at, direction: :desc
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Pagination
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
paginate 25
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Columns
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
columns do
|
|
140
|
+
column :email
|
|
141
|
+
column :job_listings, ->(u) { u.job_listings.count }
|
|
142
|
+
end
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`column` options:
|
|
146
|
+
|
|
147
|
+
- `header:` string (defaults to a humanized name)
|
|
148
|
+
- `class:` css class for the cell
|
|
149
|
+
- `render:` custom render key (advanced)
|
|
150
|
+
- `type:` `:toggle` or `:label` (special rendering)
|
|
151
|
+
- `toggle_field:` field to flip when `type: :toggle`
|
|
152
|
+
- `label_color:` color for `type: :label` (Symbol or Proc)
|
|
153
|
+
- `label_size:` `:sm`/`:md` (or Proc)
|
|
154
|
+
- `sortable:` boolean (reserved for future per-column sorting UI)
|
|
155
|
+
|
|
156
|
+
### Filters
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
filters do
|
|
160
|
+
filter :status, type: :select, options: [["Active", "active"], ["Inactive", "inactive"]]
|
|
161
|
+
end
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Filter options:
|
|
165
|
+
|
|
166
|
+
- `type:` (default `:text`)
|
|
167
|
+
- `label:`
|
|
168
|
+
- `placeholder:`
|
|
169
|
+
- `options:` / `collection:` (for select-like UI)
|
|
170
|
+
- `field:` which model field to filter on (defaults to the filter name)
|
|
171
|
+
- `apply:` Proc that receives the scope (advanced)
|
|
172
|
+
|
|
173
|
+
### Stats
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
stats do
|
|
177
|
+
stat :total, -> { User.count }, color: :slate
|
|
178
|
+
end
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Form DSL (`form do ... end`)
|
|
182
|
+
|
|
183
|
+
```ruby
|
|
184
|
+
form do
|
|
185
|
+
field :name, required: true
|
|
186
|
+
field :website, type: :url
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
See [Fields](fields.md) for supported types and options.
|
|
191
|
+
|
|
192
|
+
AdminSuite also supports basic layout helpers in forms:
|
|
193
|
+
|
|
194
|
+
- `section "Title" do ... end`
|
|
195
|
+
- `row cols: 2 do ... end`
|
|
196
|
+
|
|
197
|
+
## Show DSL (`show do ... end`)
|
|
198
|
+
|
|
199
|
+
Show is section-based. Sections can live in the main column or sidebar.
|
|
200
|
+
|
|
201
|
+
```ruby
|
|
202
|
+
show do
|
|
203
|
+
main do
|
|
204
|
+
panel :details, title: "Details", fields: %i[name email]
|
|
205
|
+
panel :related, title: "Projects", association: :projects, display: :table, columns: %i[id name status], paginate: true
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
sidebar do
|
|
209
|
+
panel :meta, title: "Meta", fields: %i[id created_at updated_at]
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Panel options:
|
|
215
|
+
|
|
216
|
+
- `title:` (defaults to a humanized name)
|
|
217
|
+
- `fields:` array of field names to display
|
|
218
|
+
- `render:` custom renderer key (see `custom_renderers` in [Configuration](configuration.md))
|
|
219
|
+
- `association:` association name to render (`has_many`, `belongs_to`, etc.)
|
|
220
|
+
- `display:` `:list` (default), `:table`, or `:cards` for associations
|
|
221
|
+
- `columns:` columns for association `:table` display
|
|
222
|
+
- `link_to:` helper method name to build links for association items (optional)
|
|
223
|
+
- `paginate:` boolean, enables pagination within the association section
|
|
224
|
+
- `per_page:` items per page for association pagination
|
|
225
|
+
- `limit:` max items (if not paginating)
|
|
226
|
+
- `collapsible:` / `collapsed:` (reserved for future UI toggles)
|
|
227
|
+
|
|
228
|
+
## Actions DSL (`actions do ... end`)
|
|
229
|
+
|
|
230
|
+
```ruby
|
|
231
|
+
actions do
|
|
232
|
+
action :reindex, label: "Reindex", method: :post, confirm: "Reindex this record?"
|
|
233
|
+
end
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
See [Actions](actions.md) for how actions execute and how to define handlers.
|
|
237
|
+
|
data/docs/theming.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Theming & assets
|
|
2
|
+
|
|
3
|
+
AdminSuite is designed to work in two modes:
|
|
4
|
+
|
|
5
|
+
1. **Engine-build mode (default)**: AdminSuite builds and ships its own Tailwind CSS into your host app at `assets:precompile` time.
|
|
6
|
+
2. **Host-themed mode (optional)**: your host app includes a stylesheet after AdminSuite for branding/overrides.
|
|
7
|
+
|
|
8
|
+
## Theme colors (`config.theme`)
|
|
9
|
+
|
|
10
|
+
AdminSuite uses CSS variables scoped to `body.admin-suite`.
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
AdminSuite.configure do |config|
|
|
14
|
+
config.theme = { primary: :emerald, secondary: :cyan }
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Allowed values
|
|
19
|
+
|
|
20
|
+
- **Named colors**: symbols/strings like `:indigo`, `:emerald`, `:cyan`, `:amber`, `:violet`, `:slate`, etc.
|
|
21
|
+
- **Hex**: `"#4f46e5"` (uses that exact color as primary/secondary in key places)
|
|
22
|
+
|
|
23
|
+
The theme primarily drives:
|
|
24
|
+
|
|
25
|
+
- Primary links/buttons (`--admin-suite-primary`, `--admin-suite-primary-hover`)
|
|
26
|
+
- Sidebar gradient (`--admin-suite-sidebar-from/via/to`)
|
|
27
|
+
|
|
28
|
+
## Host stylesheet (`config.host_stylesheet`)
|
|
29
|
+
|
|
30
|
+
If your host app already has Tailwind (or you want to override the engine UI), you can include an additional stylesheet after AdminSuite in the engine layout:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
AdminSuite.configure do |config|
|
|
34
|
+
config.host_stylesheet = :app
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This calls `stylesheet_link_tag :app` after `admin_suite.css` and `admin_suite_tailwind.css`.
|
|
39
|
+
|
|
40
|
+
## Tailwind build
|
|
41
|
+
|
|
42
|
+
AdminSuite writes an engine stylesheet into your host app during `assets:precompile`:
|
|
43
|
+
|
|
44
|
+
- Input: `AdminSuite::Engine.root/app/assets/tailwind/admin_suite.css`
|
|
45
|
+
- Output: `Rails.root/app/assets/builds/admin_suite_tailwind.css`
|
|
46
|
+
|
|
47
|
+
In development, the engine also makes a best-effort to create the output file if it’s missing, so the UI stays usable.
|
|
48
|
+
|
|
49
|
+
## Icons
|
|
50
|
+
|
|
51
|
+
AdminSuite uses lucide icons by default via `lucide-rails`.
|
|
52
|
+
|
|
53
|
+
If you need a different icon provider:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
AdminSuite.configure do |config|
|
|
57
|
+
config.icon_renderer = ->(name, view, **opts) do
|
|
58
|
+
# return HTML-safe SVG (string or ActiveSupport::SafeBuffer)
|
|
59
|
+
view.content_tag(:span, name, class: opts[:class])
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|