plutonium 0.33.1 → 0.34.0
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/# Plutonium: The pre-alpha demo.md +4 -2
- data/.claude/skills/assets/SKILL.md +416 -0
- data/.claude/skills/connect-resource/SKILL.md +112 -0
- data/.claude/skills/controller/SKILL.md +302 -0
- data/.claude/skills/create-resource/SKILL.md +240 -0
- data/.claude/skills/definition/SKILL.md +218 -0
- data/.claude/skills/definition-actions/SKILL.md +386 -0
- data/.claude/skills/definition-fields/SKILL.md +474 -0
- data/.claude/skills/definition-query/SKILL.md +334 -0
- data/.claude/skills/forms/SKILL.md +439 -0
- data/.claude/skills/installation/SKILL.md +300 -0
- data/.claude/skills/interaction/SKILL.md +382 -0
- data/.claude/skills/model/SKILL.md +267 -0
- data/.claude/skills/model-features/SKILL.md +286 -0
- data/.claude/skills/nested-resources/SKILL.md +274 -0
- data/.claude/skills/package/SKILL.md +191 -0
- data/.claude/skills/policy/SKILL.md +352 -0
- data/.claude/skills/portal/SKILL.md +400 -0
- data/.claude/skills/resource/SKILL.md +281 -0
- data/.claude/skills/rodauth/SKILL.md +452 -0
- data/.claude/skills/views/SKILL.md +563 -0
- data/Appraisals +46 -4
- data/CHANGELOG.md +32 -1
- data/app/assets/plutonium.css +2 -2
- data/config/brakeman.ignore +239 -0
- data/config/initializers/action_policy.rb +1 -1
- data/docs/.vitepress/config.ts +132 -47
- data/docs/concepts/architecture.md +226 -0
- data/docs/concepts/auto-detection.md +254 -0
- data/docs/concepts/index.md +61 -0
- data/docs/concepts/packages-portals.md +304 -0
- data/docs/concepts/resources.md +224 -0
- data/docs/cookbook/blog.md +412 -0
- data/docs/cookbook/index.md +289 -0
- data/docs/cookbook/saas.md +481 -0
- data/docs/getting-started/index.md +56 -0
- data/docs/getting-started/installation.md +146 -0
- data/docs/getting-started/tutorial/01-setup.md +118 -0
- data/docs/getting-started/tutorial/02-first-resource.md +180 -0
- data/docs/getting-started/tutorial/03-authentication.md +246 -0
- data/docs/getting-started/tutorial/04-authorization.md +170 -0
- data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
- data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
- data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
- data/docs/getting-started/tutorial/index.md +64 -0
- data/docs/guides/adding-resources.md +420 -0
- data/docs/guides/authentication.md +551 -0
- data/docs/guides/authorization.md +468 -0
- data/docs/guides/creating-packages.md +380 -0
- data/docs/guides/custom-actions.md +523 -0
- data/docs/guides/index.md +45 -0
- data/docs/guides/multi-tenancy.md +302 -0
- data/docs/guides/nested-resources.md +411 -0
- data/docs/guides/search-filtering.md +266 -0
- data/docs/guides/theming.md +321 -0
- data/docs/index.md +67 -26
- data/docs/public/CLAUDE.md +64 -21
- data/docs/reference/assets/index.md +496 -0
- data/docs/reference/controller/index.md +363 -0
- data/docs/reference/definition/actions.md +400 -0
- data/docs/reference/definition/fields.md +350 -0
- data/docs/reference/definition/index.md +252 -0
- data/docs/reference/definition/query.md +342 -0
- data/docs/reference/generators/index.md +469 -0
- data/docs/reference/index.md +49 -0
- data/docs/reference/interaction/index.md +445 -0
- data/docs/reference/model/features.md +248 -0
- data/docs/reference/model/index.md +219 -0
- data/docs/reference/policy/index.md +385 -0
- data/docs/reference/portal/index.md +382 -0
- data/docs/reference/views/forms.md +396 -0
- data/docs/reference/views/index.md +479 -0
- data/gemfiles/rails_7.gemfile +9 -2
- data/gemfiles/rails_7.gemfile.lock +146 -111
- data/gemfiles/rails_8.0.gemfile +20 -0
- data/gemfiles/rails_8.0.gemfile.lock +417 -0
- data/gemfiles/rails_8.1.gemfile +20 -0
- data/gemfiles/rails_8.1.gemfile.lock +419 -0
- data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
- data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
- data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
- data/lib/generators/pu/pkg/portal/USAGE +65 -0
- data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
- data/lib/generators/pu/res/conn/USAGE +71 -0
- data/lib/generators/pu/res/model/USAGE +106 -110
- data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
- data/lib/generators/pu/res/scaffold/USAGE +85 -0
- data/lib/generators/pu/rodauth/install_generator.rb +2 -6
- data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
- data/lib/generators/pu/skills/sync/USAGE +14 -0
- data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
- data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
- data/lib/plutonium/core/controller.rb +2 -2
- data/lib/plutonium/interaction/base.rb +1 -0
- data/lib/plutonium/package/engine.rb +2 -2
- data/lib/plutonium/query/adhoc_block.rb +6 -2
- data/lib/plutonium/query/model_scope.rb +1 -1
- data/lib/plutonium/railtie.rb +4 -0
- data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
- data/lib/plutonium/resource/query_object.rb +38 -8
- data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
- data/lib/plutonium/version.rb +1 -1
- data/lib/tasks/release.rake +19 -4
- data/package.json +1 -1
- metadata +76 -39
- data/brakeman.ignore +0 -28
- data/docs/api-examples.md +0 -49
- data/docs/guide/claude-code-guide.md +0 -74
- data/docs/guide/deep-dive/authorization.md +0 -189
- data/docs/guide/deep-dive/multitenancy.md +0 -256
- data/docs/guide/deep-dive/resources.md +0 -390
- data/docs/guide/getting-started/01-installation.md +0 -165
- data/docs/guide/index.md +0 -28
- data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
- data/docs/guide/introduction/02-core-concepts.md +0 -440
- data/docs/guide/tutorial/01-project-setup.md +0 -75
- data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
- data/docs/guide/tutorial/03-defining-resources.md +0 -90
- data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
- data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
- data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
- data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
- data/docs/markdown-examples.md +0 -85
- data/docs/modules/action.md +0 -244
- data/docs/modules/authentication.md +0 -236
- data/docs/modules/configuration.md +0 -599
- data/docs/modules/controller.md +0 -443
- data/docs/modules/core.md +0 -316
- data/docs/modules/definition.md +0 -1308
- data/docs/modules/display.md +0 -759
- data/docs/modules/form.md +0 -495
- data/docs/modules/generator.md +0 -400
- data/docs/modules/index.md +0 -167
- data/docs/modules/interaction.md +0 -642
- data/docs/modules/package.md +0 -151
- data/docs/modules/policy.md +0 -176
- data/docs/modules/portal.md +0 -710
- data/docs/modules/query.md +0 -297
- data/docs/modules/resource_record.md +0 -618
- data/docs/modules/routing.md +0 -690
- data/docs/modules/table.md +0 -301
- data/docs/modules/ui.md +0 -631
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Search and Filtering
|
|
2
|
+
|
|
3
|
+
This guide covers implementing search, filters, scopes, and sorting.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Plutonium provides built-in support for:
|
|
8
|
+
- **Search** - Full-text search across fields
|
|
9
|
+
- **Filters** - Input filters for specific fields
|
|
10
|
+
- **Scopes** - Predefined query shortcuts (quick filter buttons)
|
|
11
|
+
- **Sorting** - Column-based ordering
|
|
12
|
+
|
|
13
|
+
## Search
|
|
14
|
+
|
|
15
|
+
Define global search in the definition:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
class PostDefinition < ResourceDefinition
|
|
19
|
+
search do |scope, query|
|
|
20
|
+
scope.where("title ILIKE ?", "%#{query}%")
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Multi-Field Search
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
search do |scope, query|
|
|
29
|
+
scope.where(
|
|
30
|
+
"title ILIKE :q OR content ILIKE :q OR author_name ILIKE :q",
|
|
31
|
+
q: "%#{query}%"
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Search with Associations
|
|
37
|
+
|
|
38
|
+
```ruby
|
|
39
|
+
search do |scope, query|
|
|
40
|
+
scope.joins(:author).where(
|
|
41
|
+
"posts.title ILIKE :q OR users.name ILIKE :q",
|
|
42
|
+
q: "%#{query}%"
|
|
43
|
+
).distinct
|
|
44
|
+
end
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Split Search Terms
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
search do |scope, query|
|
|
51
|
+
terms = query.split(/\s+/)
|
|
52
|
+
terms.reduce(scope) do |current_scope, term|
|
|
53
|
+
current_scope.where("title ILIKE ?", "%#{term}%")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Full-Text Search (PostgreSQL)
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
search do |scope, query|
|
|
62
|
+
scope.where(
|
|
63
|
+
"to_tsvector('english', title || ' ' || body) @@ plainto_tsquery('english', ?)",
|
|
64
|
+
query
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Filters
|
|
70
|
+
|
|
71
|
+
Filters provide UI controls for narrowing results.
|
|
72
|
+
|
|
73
|
+
### Text Filter
|
|
74
|
+
|
|
75
|
+
The built-in `Text` filter supports various predicates:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
class PostDefinition < ResourceDefinition
|
|
79
|
+
# Exact match
|
|
80
|
+
filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
|
|
81
|
+
|
|
82
|
+
# Contains (LIKE %value%)
|
|
83
|
+
filter :title, with: Plutonium::Query::Filters::Text, predicate: :contains
|
|
84
|
+
|
|
85
|
+
# Starts with
|
|
86
|
+
filter :slug, with: Plutonium::Query::Filters::Text, predicate: :starts_with
|
|
87
|
+
|
|
88
|
+
# Ends with
|
|
89
|
+
filter :email, with: Plutonium::Query::Filters::Text, predicate: :ends_with
|
|
90
|
+
end
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Available Predicates
|
|
94
|
+
|
|
95
|
+
| Predicate | SQL | Description |
|
|
96
|
+
|-----------|-----|-------------|
|
|
97
|
+
| `:eq` | `= value` | Exact match |
|
|
98
|
+
| `:not_eq` | `!= value` | Not equal |
|
|
99
|
+
| `:contains` | `LIKE %value%` | Contains text |
|
|
100
|
+
| `:not_contains` | `NOT LIKE %value%` | Does not contain |
|
|
101
|
+
| `:starts_with` | `LIKE value%` | Starts with |
|
|
102
|
+
| `:ends_with` | `LIKE %value` | Ends with |
|
|
103
|
+
| `:matches` | `LIKE value` | Pattern match (`*` becomes `%`) |
|
|
104
|
+
| `:not_matches` | `NOT LIKE value` | Does not match pattern |
|
|
105
|
+
|
|
106
|
+
### Custom Filter with Lambda
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
filter :published, with: ->(scope, value) {
|
|
110
|
+
value == "true" ? scope.where.not(published_at: nil) : scope.where(published_at: nil)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
filter :has_comments, with: ->(scope, value) {
|
|
114
|
+
if value == "true"
|
|
115
|
+
scope.joins(:comments).distinct
|
|
116
|
+
else
|
|
117
|
+
scope.left_joins(:comments).where(comments: { id: nil })
|
|
118
|
+
end
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Custom Filter Class
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
class DateRangeFilter < Plutonium::Query::Filter
|
|
126
|
+
def apply(scope, start_date: nil, end_date: nil)
|
|
127
|
+
scope = scope.where("#{key} >= ?", start_date.beginning_of_day) if start_date.present?
|
|
128
|
+
scope = scope.where("#{key} <= ?", end_date.end_of_day) if end_date.present?
|
|
129
|
+
scope
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def customize_inputs
|
|
133
|
+
input :start_date, as: :date
|
|
134
|
+
input :end_date, as: :date
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Use in definition
|
|
139
|
+
filter :created_at, with: DateRangeFilter
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Scopes
|
|
143
|
+
|
|
144
|
+
Scopes appear as quick filter buttons. They reference model scopes or use inline blocks.
|
|
145
|
+
|
|
146
|
+
### Basic Scopes
|
|
147
|
+
|
|
148
|
+
Reference existing model scopes:
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
class PostDefinition < ResourceDefinition
|
|
152
|
+
scope :published # Uses Post.published
|
|
153
|
+
scope :draft # Uses Post.draft
|
|
154
|
+
scope :featured # Uses Post.featured
|
|
155
|
+
end
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Inline Scopes
|
|
159
|
+
|
|
160
|
+
Use block syntax with the scope passed as an argument:
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
scope(:recent) { |scope| scope.where("created_at > ?", 1.week.ago) }
|
|
164
|
+
scope(:this_month) { |scope| scope.where(created_at: Time.current.all_month) }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### With Controller Context
|
|
168
|
+
|
|
169
|
+
Inline scopes have access to controller context like `current_user`:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
scope(:mine) { |scope| scope.where(author: current_user) }
|
|
173
|
+
scope(:my_team) { |scope| scope.where(team: current_user.team) }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Default Scope
|
|
177
|
+
|
|
178
|
+
Set a scope as default:
|
|
179
|
+
|
|
180
|
+
```ruby
|
|
181
|
+
class PostDefinition < ResourceDefinition
|
|
182
|
+
scope :published, default: true # Applied by default
|
|
183
|
+
scope :draft
|
|
184
|
+
scope :archived
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
When a default scope is set:
|
|
189
|
+
- The default scope is applied on initial page load
|
|
190
|
+
- The default scope button is highlighted (not "All")
|
|
191
|
+
- Clicking "All" shows all records without any scope filter
|
|
192
|
+
|
|
193
|
+
## Sorting
|
|
194
|
+
|
|
195
|
+
### Define Sortable Fields
|
|
196
|
+
|
|
197
|
+
```ruby
|
|
198
|
+
class PostDefinition < ResourceDefinition
|
|
199
|
+
sort :title
|
|
200
|
+
sort :created_at
|
|
201
|
+
sort :view_count
|
|
202
|
+
|
|
203
|
+
# Multiple at once
|
|
204
|
+
sorts :title, :created_at, :view_count
|
|
205
|
+
end
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### Default Sort Order
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
# Field and direction
|
|
212
|
+
default_sort :created_at, :desc
|
|
213
|
+
default_sort :title, :asc
|
|
214
|
+
|
|
215
|
+
# Complex sorting with block
|
|
216
|
+
default_sort { |scope| scope.order(featured: :desc, created_at: :desc) }
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**Note:** Default sort only applies when no sort params are provided. The system default is `:id, :desc`.
|
|
220
|
+
|
|
221
|
+
## URL Parameters
|
|
222
|
+
|
|
223
|
+
Query parameters are structured under `q`:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
/posts?q[search]=rails
|
|
227
|
+
/posts?q[status][query]=published
|
|
228
|
+
/posts?q[scope]=recent
|
|
229
|
+
/posts?q[sort_fields][]=created_at&q[sort_directions][created_at]=desc
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Complete Example
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
class PostDefinition < ResourceDefinition
|
|
236
|
+
# Full-text search
|
|
237
|
+
search do |scope, query|
|
|
238
|
+
scope.where(
|
|
239
|
+
"title ILIKE :q OR content ILIKE :q",
|
|
240
|
+
q: "%#{query}%"
|
|
241
|
+
)
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Filters
|
|
245
|
+
filter :status, with: Plutonium::Query::Filters::Text, predicate: :eq
|
|
246
|
+
filter :category, with: Plutonium::Query::Filters::Text, predicate: :eq
|
|
247
|
+
filter :title, with: Plutonium::Query::Filters::Text, predicate: :contains
|
|
248
|
+
|
|
249
|
+
# Quick scopes (reference model scopes)
|
|
250
|
+
scope :published
|
|
251
|
+
scope :draft
|
|
252
|
+
scope :featured
|
|
253
|
+
scope(:recent) { |scope| scope.where("created_at > ?", 1.week.ago) }
|
|
254
|
+
|
|
255
|
+
# Sortable columns
|
|
256
|
+
sorts :title, :created_at, :view_count, :published_at
|
|
257
|
+
|
|
258
|
+
# Default: newest first
|
|
259
|
+
default_sort :created_at, :desc
|
|
260
|
+
end
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Related
|
|
264
|
+
|
|
265
|
+
- [Custom Actions](./custom-actions)
|
|
266
|
+
- [Authorization](./authorization)
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
# Theming
|
|
2
|
+
|
|
3
|
+
This guide covers customizing colors, styles, and branding.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Plutonium uses TailwindCSS 4 for styling. Customization happens through:
|
|
8
|
+
|
|
9
|
+
- **Tailwind Configuration** - Extend colors and design tokens
|
|
10
|
+
- **Component Themes** - Override form, table, and display styling
|
|
11
|
+
- **Asset Configuration** - Custom CSS, JS, logo, and favicon
|
|
12
|
+
|
|
13
|
+
## Setup Custom Assets
|
|
14
|
+
|
|
15
|
+
Run the assets generator to set up your own TailwindCSS build:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
rails generate pu:core:assets
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This:
|
|
22
|
+
1. Installs required npm packages (`@radioactive-labs/plutonium`, TailwindCSS plugins)
|
|
23
|
+
2. Creates `tailwind.config.js` that extends Plutonium's config
|
|
24
|
+
3. Imports Plutonium CSS into your `application.tailwind.css`
|
|
25
|
+
4. Registers Plutonium's Stimulus controllers
|
|
26
|
+
5. Updates Plutonium config to use your assets
|
|
27
|
+
|
|
28
|
+
## Asset Configuration
|
|
29
|
+
|
|
30
|
+
Configure assets in the initializer:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
# config/initializers/plutonium.rb
|
|
34
|
+
Plutonium.configure do |config|
|
|
35
|
+
config.load_defaults 1.0
|
|
36
|
+
|
|
37
|
+
config.assets.stylesheet = "application" # Your CSS file
|
|
38
|
+
config.assets.script = "application" # Your JS file
|
|
39
|
+
config.assets.logo = "my_logo.png" # Logo image
|
|
40
|
+
config.assets.favicon = "my_favicon.ico" # Favicon
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## TailwindCSS Configuration
|
|
45
|
+
|
|
46
|
+
### Generated Config
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
// tailwind.config.js
|
|
50
|
+
const { execSync } = require('child_process');
|
|
51
|
+
const plutoniumGemPath = execSync("bundle show plutonium").toString().trim();
|
|
52
|
+
const plutoniumTailwindConfig = require(`${plutoniumGemPath}/tailwind.options.js`)
|
|
53
|
+
|
|
54
|
+
module.exports = {
|
|
55
|
+
darkMode: plutoniumTailwindConfig.darkMode,
|
|
56
|
+
plugins: [
|
|
57
|
+
// Add your plugins here
|
|
58
|
+
].concat(plutoniumTailwindConfig.plugins),
|
|
59
|
+
theme: plutoniumTailwindConfig.merge(
|
|
60
|
+
plutoniumTailwindConfig.theme,
|
|
61
|
+
{
|
|
62
|
+
// Your custom theme overrides
|
|
63
|
+
},
|
|
64
|
+
),
|
|
65
|
+
content: [
|
|
66
|
+
`${__dirname}/app/**/*.{erb,haml,html,slim,rb}`,
|
|
67
|
+
`${__dirname}/app/javascript/**/*.js`,
|
|
68
|
+
`${__dirname}/packages/**/app/**/*.{erb,haml,html,slim,rb}`,
|
|
69
|
+
].concat(plutoniumTailwindConfig.content),
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Customizing Colors
|
|
74
|
+
|
|
75
|
+
Override Plutonium's color palette:
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
// tailwind.config.js
|
|
79
|
+
theme: plutoniumTailwindConfig.merge(
|
|
80
|
+
plutoniumTailwindConfig.theme,
|
|
81
|
+
{
|
|
82
|
+
extend: {
|
|
83
|
+
colors: {
|
|
84
|
+
primary: {
|
|
85
|
+
50: '#eff6ff',
|
|
86
|
+
100: '#dbeafe',
|
|
87
|
+
200: '#bfdbfe',
|
|
88
|
+
300: '#93c5fd',
|
|
89
|
+
400: '#60a5fa',
|
|
90
|
+
500: '#3b82f6', // Your brand color
|
|
91
|
+
600: '#2563eb',
|
|
92
|
+
700: '#1d4ed8',
|
|
93
|
+
800: '#1e40af',
|
|
94
|
+
900: '#1e3a8a',
|
|
95
|
+
950: '#172554',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
),
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Semantic Colors
|
|
104
|
+
|
|
105
|
+
Plutonium includes these semantic colors:
|
|
106
|
+
|
|
107
|
+
| Color | Usage |
|
|
108
|
+
|-------|-------|
|
|
109
|
+
| `primary` | Primary brand color |
|
|
110
|
+
| `secondary` | Secondary color |
|
|
111
|
+
| `success` | Success states (green) |
|
|
112
|
+
| `info` | Informational states (blue) |
|
|
113
|
+
| `warning` | Warning states (amber) |
|
|
114
|
+
| `danger` | Error/danger states (red) |
|
|
115
|
+
| `accent` | Accent highlights |
|
|
116
|
+
|
|
117
|
+
## CSS Imports
|
|
118
|
+
|
|
119
|
+
### Application Stylesheet
|
|
120
|
+
|
|
121
|
+
```css
|
|
122
|
+
/* app/assets/stylesheets/application.tailwind.css */
|
|
123
|
+
@import "gem:plutonium/src/css/plutonium.css";
|
|
124
|
+
|
|
125
|
+
@import "tailwindcss";
|
|
126
|
+
@config '../../../tailwind.config.js';
|
|
127
|
+
|
|
128
|
+
/* Your custom styles */
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Component Themes
|
|
132
|
+
|
|
133
|
+
Plutonium components use a theme system. Override themes by defining nested Theme classes in your definitions.
|
|
134
|
+
|
|
135
|
+
### Form Theme
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
class PostDefinition < ResourceDefinition
|
|
139
|
+
class Form < Form
|
|
140
|
+
class Theme < Plutonium::UI::Form::Theme
|
|
141
|
+
def self.theme
|
|
142
|
+
super.merge({
|
|
143
|
+
base: "bg-white dark:bg-gray-800 shadow-md rounded-lg p-6",
|
|
144
|
+
fields_wrapper: "grid grid-cols-2 gap-6",
|
|
145
|
+
actions_wrapper: "flex justify-end mt-6 space-x-2",
|
|
146
|
+
label: "block mb-2 text-base font-bold",
|
|
147
|
+
input: "w-full p-2 border rounded-md shadow-sm",
|
|
148
|
+
hint: "mt-2 text-sm text-gray-500",
|
|
149
|
+
error: "mt-2 text-sm text-red-600",
|
|
150
|
+
button: "px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700",
|
|
151
|
+
})
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Display Theme
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
class PostDefinition < ResourceDefinition
|
|
162
|
+
class Display < Display
|
|
163
|
+
class Theme < Plutonium::UI::Display::Theme
|
|
164
|
+
def self.theme
|
|
165
|
+
super.merge({
|
|
166
|
+
fields_wrapper: "grid grid-cols-3 gap-8",
|
|
167
|
+
label: "text-sm font-bold text-gray-500 mb-1",
|
|
168
|
+
string: "text-lg text-gray-900 dark:text-white",
|
|
169
|
+
link: "text-primary-600 hover:underline",
|
|
170
|
+
})
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Table Theme
|
|
178
|
+
|
|
179
|
+
```ruby
|
|
180
|
+
class PostDefinition < ResourceDefinition
|
|
181
|
+
class Table < Table
|
|
182
|
+
class Theme < Plutonium::UI::Table::Theme
|
|
183
|
+
def self.theme
|
|
184
|
+
super.merge({
|
|
185
|
+
wrapper: "overflow-x-auto shadow-md rounded-lg",
|
|
186
|
+
base: "w-full text-sm text-gray-500",
|
|
187
|
+
header: "text-xs uppercase bg-gray-100 dark:bg-gray-700",
|
|
188
|
+
header_cell: "px-6 py-3",
|
|
189
|
+
body_row: "bg-white border-b dark:bg-gray-800",
|
|
190
|
+
body_cell: "px-6 py-4",
|
|
191
|
+
})
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Branding
|
|
199
|
+
|
|
200
|
+
### Application Name
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
# config/initializers/plutonium.rb
|
|
204
|
+
Plutonium.application_name = "My Application"
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Custom Logo
|
|
208
|
+
|
|
209
|
+
Override the logo in your layout:
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
# packages/admin_portal/app/views/layouts/admin_portal/application.rb
|
|
213
|
+
module AdminPortal
|
|
214
|
+
class ApplicationLayout < Plutonium::UI::Layout::Application
|
|
215
|
+
def render_logo
|
|
216
|
+
img(src: helpers.asset_path("logo.svg"), alt: "My App", class: "h-8")
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Custom Fonts
|
|
223
|
+
|
|
224
|
+
Override in your layout:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
class MyLayout < Plutonium::UI::Layout::ResourceLayout
|
|
228
|
+
def render_fonts
|
|
229
|
+
link(rel: "preconnect", href: "https://fonts.googleapis.com")
|
|
230
|
+
link(href: "https://fonts.googleapis.com/css2?family=Inter&display=swap", rel: "stylesheet")
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Update Tailwind config:
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
theme: {
|
|
239
|
+
fontFamily: {
|
|
240
|
+
'body': ['Inter', 'sans-serif'],
|
|
241
|
+
'sans': ['Inter', 'sans-serif'],
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Dark Mode
|
|
247
|
+
|
|
248
|
+
Plutonium uses `selector` strategy for dark mode. Toggle by adding/removing the `dark` class on `<html>`:
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
document.documentElement.classList.toggle('dark');
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Plutonium includes a color mode selector component that handles this automatically.
|
|
255
|
+
|
|
256
|
+
## Stimulus Controllers
|
|
257
|
+
|
|
258
|
+
Register Plutonium's Stimulus controllers in your application:
|
|
259
|
+
|
|
260
|
+
```javascript
|
|
261
|
+
// app/javascript/controllers/index.js
|
|
262
|
+
import { application } from "./application"
|
|
263
|
+
|
|
264
|
+
import { registerControllers } from "@radioactive-labs/plutonium"
|
|
265
|
+
registerControllers(application)
|
|
266
|
+
|
|
267
|
+
// Your custom controllers...
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Available Controllers
|
|
271
|
+
|
|
272
|
+
- `color-mode` - Dark/light mode toggle
|
|
273
|
+
- `form` - Form handling
|
|
274
|
+
- `nested-resource-form-fields` - Nested form management
|
|
275
|
+
- `slim-select` - Enhanced select boxes
|
|
276
|
+
- `flatpickr` - Date/time pickers
|
|
277
|
+
- `easymde` - Markdown editor
|
|
278
|
+
|
|
279
|
+
### Custom Controllers
|
|
280
|
+
|
|
281
|
+
```javascript
|
|
282
|
+
// app/javascript/controllers/custom_controller.js
|
|
283
|
+
import { Controller } from "@hotwired/stimulus"
|
|
284
|
+
|
|
285
|
+
export default class extends Controller {
|
|
286
|
+
connect() {
|
|
287
|
+
console.log("Connected")
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Register in your index:
|
|
293
|
+
|
|
294
|
+
```javascript
|
|
295
|
+
import CustomController from "./custom_controller"
|
|
296
|
+
application.register("custom", CustomController)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Portal-Specific Themes
|
|
300
|
+
|
|
301
|
+
Different portals can have different themes by overriding definitions per-portal:
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
# packages/admin_portal/app/definitions/admin_portal/post_definition.rb
|
|
305
|
+
class AdminPortal::PostDefinition < ::PostDefinition
|
|
306
|
+
class Form < Form
|
|
307
|
+
class Theme < Plutonium::UI::Form::Theme
|
|
308
|
+
def self.theme
|
|
309
|
+
super.merge({
|
|
310
|
+
base: "bg-blue-50 p-8", # Admin-specific styling
|
|
311
|
+
})
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Related
|
|
319
|
+
|
|
320
|
+
- [Custom Actions](./custom-actions)
|
|
321
|
+
- [Creating Packages](./creating-packages)
|
data/docs/index.md
CHANGED
|
@@ -1,40 +1,81 @@
|
|
|
1
1
|
---
|
|
2
|
-
# https://vitepress.dev/reference/default-theme-home-page
|
|
3
2
|
layout: home
|
|
4
|
-
|
|
5
3
|
hero:
|
|
6
4
|
name: Plutonium
|
|
7
|
-
text:
|
|
8
|
-
tagline: Build
|
|
9
|
-
image:
|
|
10
|
-
src: /plutonium.png
|
|
11
|
-
alt: Plutonium
|
|
5
|
+
text: Rapid Application Development for Rails
|
|
6
|
+
tagline: Build production-ready Rails applications in minutes, not months. Convention-driven, fully customizable.
|
|
12
7
|
actions:
|
|
13
8
|
- theme: brand
|
|
14
|
-
text:
|
|
15
|
-
link: /
|
|
9
|
+
text: Get Started
|
|
10
|
+
link: /getting-started/
|
|
16
11
|
- theme: alt
|
|
17
|
-
text:
|
|
18
|
-
link: /
|
|
12
|
+
text: View on GitHub
|
|
13
|
+
link: https://github.com/radioactive-labs/plutonium-core
|
|
19
14
|
|
|
20
15
|
features:
|
|
21
16
|
- icon: 🚀
|
|
22
|
-
title:
|
|
23
|
-
details:
|
|
24
|
-
- icon:
|
|
25
|
-
title:
|
|
26
|
-
details:
|
|
17
|
+
title: Zero to Production Fast
|
|
18
|
+
details: Generate complete CRUD interfaces with a single command. Authentication, authorization, and UI included out of the box.
|
|
19
|
+
- icon: 🧩
|
|
20
|
+
title: Modular Architecture
|
|
21
|
+
details: Organize your app into Feature Packages and Portals. Each module is isolated, testable, and reusable.
|
|
27
22
|
- icon: 🎨
|
|
28
|
-
title:
|
|
29
|
-
details:
|
|
30
|
-
|
|
23
|
+
title: Fully Customizable
|
|
24
|
+
details: Every layer is overridable. Customize fields, forms, tables, pages, and styles without fighting the framework.
|
|
25
|
+
- icon: 🔐
|
|
26
|
+
title: Built-in Authorization
|
|
27
|
+
details: Policy-based authorization at every level - actions, attributes, and collection scoping. Multi-tenancy ready.
|
|
28
|
+
- icon: 📦
|
|
29
|
+
title: Convention over Configuration
|
|
30
|
+
details: Smart defaults detect your models, associations, and validations. Only configure what you need to change.
|
|
31
|
+
- icon: 🛠️
|
|
32
|
+
title: Rails Native
|
|
33
|
+
details: Built on Rails conventions. Uses Phlex for views, Rodauth for auth, and standard Rails patterns throughout.
|
|
31
34
|
---
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
## Why Plutonium?
|
|
37
|
+
|
|
38
|
+
Building admin panels, dashboards, and internal tools in Rails often means:
|
|
39
|
+
- Writing repetitive CRUD code
|
|
40
|
+
- Building authorization from scratch
|
|
41
|
+
- Creating forms and tables manually
|
|
42
|
+
- Handling multi-tenancy yourself
|
|
43
|
+
|
|
44
|
+
**Plutonium eliminates this boilerplate** while remaining fully customizable.
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Create a new Plutonium app
|
|
48
|
+
rails new myapp -m https://radioactive-labs.github.io/plutonium-core/templates/plutonium.rb
|
|
49
|
+
|
|
50
|
+
# Generate a resource
|
|
51
|
+
rails g pu:res:scaffold Post title:string body:text published:boolean
|
|
52
|
+
|
|
53
|
+
# Connect it to a portal
|
|
54
|
+
rails g pu:res:conn Post --portal AdminPortal
|
|
55
|
+
|
|
56
|
+
# Done. Full CRUD with auth, policies, and UI.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## How It Works
|
|
60
|
+
|
|
61
|
+
Plutonium follows a layered architecture where each layer has a single responsibility:
|
|
62
|
+
|
|
63
|
+
| Layer | Purpose | File |
|
|
64
|
+
|-------|---------|------|
|
|
65
|
+
| **Model** | Data structure and validations | `app/models/post.rb` |
|
|
66
|
+
| **Definition** | How the resource renders (fields, actions) | `app/definitions/post_definition.rb` |
|
|
67
|
+
| **Policy** | Who can do what | `app/policies/post_policy.rb` |
|
|
68
|
+
| **Controller** | HTTP handling and customization | `app/controllers/posts_controller.rb` |
|
|
69
|
+
|
|
70
|
+
Each layer auto-detects sensible defaults. You only write code when you need to customize.
|
|
71
|
+
|
|
72
|
+
## Quick Links
|
|
73
|
+
|
|
74
|
+
<div class="quick-links">
|
|
75
|
+
|
|
76
|
+
- [Installation Guide](/getting-started/installation) - Set up Plutonium in a new or existing Rails app
|
|
77
|
+
- [Tutorial](/getting-started/tutorial/) - Build a complete blog application step by step
|
|
78
|
+
- [Architecture Overview](/concepts/architecture) - Understand how the pieces fit together
|
|
79
|
+
- [Reference Documentation](/reference/model/) - Detailed API documentation
|
|
37
80
|
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
</style>
|
|
81
|
+
</div>
|