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,396 @@
|
|
|
1
|
+
# Forms Reference
|
|
2
|
+
|
|
3
|
+
Complete reference for form customization using Phlexi::Form.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Plutonium forms are built on [Phlexi::Form](https://github.com/radioactive-labs/phlexi-form), providing a Ruby-first approach to form building.
|
|
8
|
+
|
|
9
|
+
## Form Class Hierarchy
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Phlexi::Form::Base
|
|
13
|
+
└── Plutonium::UI::Form::Base # Base with Plutonium components
|
|
14
|
+
├── Plutonium::UI::Form::Resource # Resource CRUD forms
|
|
15
|
+
│ └── Plutonium::UI::Form::Interaction # Interactive action forms
|
|
16
|
+
└── Plutonium::UI::Form::Query # Search/filter forms
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Custom Form Template
|
|
20
|
+
|
|
21
|
+
Override form rendering in your definition:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
class PostDefinition < ResourceDefinition
|
|
25
|
+
class Form < Form
|
|
26
|
+
def form_template
|
|
27
|
+
# Your custom layout
|
|
28
|
+
render_fields
|
|
29
|
+
render_actions
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Form Template Methods
|
|
36
|
+
|
|
37
|
+
| Method | Description |
|
|
38
|
+
|--------|-------------|
|
|
39
|
+
| `form_template` | Main template method to override |
|
|
40
|
+
| `render_fields` | Render all permitted fields |
|
|
41
|
+
| `render_resource_field(name)` | Render a single field by name |
|
|
42
|
+
| `render_actions` | Render submit buttons |
|
|
43
|
+
| `fields_wrapper { }` | Wrapper div for field grid |
|
|
44
|
+
| `actions_wrapper { }` | Wrapper div for buttons |
|
|
45
|
+
|
|
46
|
+
### Form Attributes
|
|
47
|
+
|
|
48
|
+
| Attribute | Description |
|
|
49
|
+
|-----------|-------------|
|
|
50
|
+
| `object` / `record` | The form object being edited |
|
|
51
|
+
| `resource_fields` | Array of permitted field names |
|
|
52
|
+
| `resource_definition` | The definition instance |
|
|
53
|
+
|
|
54
|
+
## Custom Layouts
|
|
55
|
+
|
|
56
|
+
### Sectioned Form
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
class PostDefinition < ResourceDefinition
|
|
60
|
+
class Form < Form
|
|
61
|
+
def form_template
|
|
62
|
+
section("Basic Information") {
|
|
63
|
+
render_resource_field :title
|
|
64
|
+
render_resource_field :slug
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
section("Content") {
|
|
68
|
+
render_resource_field :content
|
|
69
|
+
render_resource_field :excerpt
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
render_actions
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def section(title, &)
|
|
78
|
+
div(class: "mb-8") {
|
|
79
|
+
h3(class: "text-lg font-semibold mb-4") { title }
|
|
80
|
+
fields_wrapper(&)
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Two-Column Layout
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
class Form < Form
|
|
91
|
+
def form_template
|
|
92
|
+
div(class: "grid grid-cols-1 lg:grid-cols-3 gap-6") {
|
|
93
|
+
# Main content - 2 columns
|
|
94
|
+
div(class: "lg:col-span-2") {
|
|
95
|
+
fields_wrapper {
|
|
96
|
+
render_resource_field :title
|
|
97
|
+
render_resource_field :content
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
# Sidebar - 1 column
|
|
102
|
+
div(class: "space-y-4") {
|
|
103
|
+
Panel {
|
|
104
|
+
h4(class: "font-medium mb-2") { "Settings" }
|
|
105
|
+
render_resource_field :status
|
|
106
|
+
render_resource_field :visibility
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
render_actions
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Field Builder
|
|
117
|
+
|
|
118
|
+
When using `render_resource_field`, Plutonium applies definition configuration. For custom rendering, use the `field` method directly.
|
|
119
|
+
|
|
120
|
+
### Basic Field Usage
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
def form_template
|
|
124
|
+
# Using field builder directly
|
|
125
|
+
render field(:title).wrapped { |f| f.input_tag }
|
|
126
|
+
render field(:content).wrapped { |f| f.easymde_tag }
|
|
127
|
+
render field(:published).wrapped { |f| f.checkbox_tag }
|
|
128
|
+
|
|
129
|
+
render_actions
|
|
130
|
+
end
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Standard Tag Methods
|
|
134
|
+
|
|
135
|
+
| Method | Input Type |
|
|
136
|
+
|--------|------------|
|
|
137
|
+
| `f.input_tag` | Text input (auto-detects type) |
|
|
138
|
+
| `f.string_tag` | Text input |
|
|
139
|
+
| `f.text_tag` | Textarea |
|
|
140
|
+
| `f.number_tag` | Number input |
|
|
141
|
+
| `f.email_tag` | Email input |
|
|
142
|
+
| `f.password_tag` | Password input |
|
|
143
|
+
| `f.url_tag` | URL input |
|
|
144
|
+
| `f.phone_tag` | Telephone input |
|
|
145
|
+
| `f.hidden_tag` | Hidden input |
|
|
146
|
+
| `f.date_tag` | Date input |
|
|
147
|
+
| `f.time_tag` | Time input |
|
|
148
|
+
| `f.datetime_tag` | Datetime input |
|
|
149
|
+
| `f.checkbox_tag` | Checkbox |
|
|
150
|
+
| `f.boolean_tag` | Checkbox (themed as boolean) |
|
|
151
|
+
| `f.select_tag` | Select dropdown |
|
|
152
|
+
| `f.radio_button_tag` | Radio button |
|
|
153
|
+
| `f.collection_radio_buttons_tag` | Radio button collection |
|
|
154
|
+
| `f.collection_checkboxes_tag` | Checkbox collection |
|
|
155
|
+
| `f.range_tag` | Range slider |
|
|
156
|
+
| `f.file_input_tag` | File input |
|
|
157
|
+
|
|
158
|
+
### Plutonium-Enhanced Tags
|
|
159
|
+
|
|
160
|
+
Plutonium extends the form builder with additional tags. See the [Form::Base::Builder source](https://github.com/radioactive-labs/plutonium-core/blob/master/lib/plutonium/ui/form/base.rb) for the current list.
|
|
161
|
+
|
|
162
|
+
Common ones include:
|
|
163
|
+
- `f.easymde_tag` / `f.markdown_tag` - Markdown editor
|
|
164
|
+
- `f.slim_select_tag` - Enhanced select
|
|
165
|
+
- `f.flatpickr_tag` - Date/time picker
|
|
166
|
+
- `f.uppy_tag` / `f.file_tag` - File upload
|
|
167
|
+
- `f.secure_association_tag` - Association with SGID
|
|
168
|
+
|
|
169
|
+
### Field with Options
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
# Select with choices
|
|
173
|
+
render field(:status).wrapped { |f|
|
|
174
|
+
f.select_tag(choices: %w[draft published archived])
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
# Date picker with options
|
|
178
|
+
render field(:published_at).wrapped { |f|
|
|
179
|
+
f.flatpickr_tag(min_date: Date.today, enable_time: true)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# File upload with restrictions
|
|
183
|
+
render field(:avatar).wrapped { |f|
|
|
184
|
+
f.uppy_tag(
|
|
185
|
+
allowed_file_types: %w[.jpg .png .gif],
|
|
186
|
+
max_file_size: 5.megabytes
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Wrapped vs Unwrapped
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
# Wrapped - includes label, hint, errors
|
|
195
|
+
render field(:title).wrapped { |f| f.input_tag }
|
|
196
|
+
|
|
197
|
+
# Unwrapped - just the input element
|
|
198
|
+
render field(:title).input_tag
|
|
199
|
+
|
|
200
|
+
# Custom wrapper options
|
|
201
|
+
render field(:title).wrapped(class: "col-span-full") { |f|
|
|
202
|
+
f.input_tag
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Input Configuration in Definitions
|
|
207
|
+
|
|
208
|
+
Define inputs in the definition, render them in the form:
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
class PostDefinition < ResourceDefinition
|
|
212
|
+
# Configure inputs
|
|
213
|
+
input :title, hint: "Be descriptive", placeholder: "Enter title"
|
|
214
|
+
input :content, as: :markdown
|
|
215
|
+
input :status, as: :select, choices: %w[draft published]
|
|
216
|
+
input :published_at, as: :flatpickr
|
|
217
|
+
|
|
218
|
+
# Custom input with block
|
|
219
|
+
input :category do |f|
|
|
220
|
+
choices = Category.active.pluck(:name, :id)
|
|
221
|
+
f.select_tag(choices: choices)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
class Form < Form
|
|
225
|
+
def form_template
|
|
226
|
+
# render_resource_field uses the input configuration
|
|
227
|
+
render_resource_field :title
|
|
228
|
+
render_resource_field :content
|
|
229
|
+
render_resource_field :status
|
|
230
|
+
render_resource_field :published_at
|
|
231
|
+
render_resource_field :category
|
|
232
|
+
|
|
233
|
+
render_actions
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Nested Forms
|
|
240
|
+
|
|
241
|
+
For `has_many` / `has_one` associations with `accepts_nested_attributes_for`:
|
|
242
|
+
|
|
243
|
+
### Model Setup
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
class Post < ResourceRecord
|
|
247
|
+
has_many :comments
|
|
248
|
+
accepts_nested_attributes_for :comments, allow_destroy: true
|
|
249
|
+
end
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Definition Setup
|
|
253
|
+
|
|
254
|
+
```ruby
|
|
255
|
+
class PostDefinition < ResourceDefinition
|
|
256
|
+
nested_input :comments do |n|
|
|
257
|
+
n.input :author_name
|
|
258
|
+
n.input :body, as: :text
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Or reference another definition
|
|
262
|
+
nested_input :comments, using: CommentDefinition, fields: %i[author_name body]
|
|
263
|
+
end
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Rendering
|
|
267
|
+
|
|
268
|
+
```ruby
|
|
269
|
+
class Form < Form
|
|
270
|
+
def form_template
|
|
271
|
+
render_resource_field :title
|
|
272
|
+
render_resource_field :content
|
|
273
|
+
|
|
274
|
+
# Nested fields are automatically handled
|
|
275
|
+
render_resource_field :comments
|
|
276
|
+
|
|
277
|
+
render_actions
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Dynamic Forms (pre_submit)
|
|
283
|
+
|
|
284
|
+
Fields with `pre_submit: true` trigger form re-rendering on change:
|
|
285
|
+
|
|
286
|
+
```ruby
|
|
287
|
+
class PostDefinition < ResourceDefinition
|
|
288
|
+
input :post_type, as: :select,
|
|
289
|
+
choices: %w[article video podcast],
|
|
290
|
+
pre_submit: true
|
|
291
|
+
|
|
292
|
+
input :video_url,
|
|
293
|
+
condition: -> { object.post_type == "video" }
|
|
294
|
+
|
|
295
|
+
input :podcast_url,
|
|
296
|
+
condition: -> { object.post_type == "podcast" }
|
|
297
|
+
end
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
When `post_type` changes, the form re-renders via Turbo and shows/hides conditional fields.
|
|
301
|
+
|
|
302
|
+
## Form Actions
|
|
303
|
+
|
|
304
|
+
### Default Actions
|
|
305
|
+
|
|
306
|
+
```ruby
|
|
307
|
+
def render_actions
|
|
308
|
+
actions_wrapper {
|
|
309
|
+
render submit_button
|
|
310
|
+
}
|
|
311
|
+
end
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Custom Actions
|
|
315
|
+
|
|
316
|
+
```ruby
|
|
317
|
+
def render_actions
|
|
318
|
+
actions_wrapper {
|
|
319
|
+
# Cancel link
|
|
320
|
+
a(href: resource_url_for(resource_class), class: "btn btn-secondary") {
|
|
321
|
+
"Cancel"
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
# Save as draft
|
|
325
|
+
button(type: :submit, name: "draft", value: "1", class: "btn") {
|
|
326
|
+
"Save Draft"
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
# Primary submit
|
|
330
|
+
render submit_button
|
|
331
|
+
}
|
|
332
|
+
end
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Form Context
|
|
336
|
+
|
|
337
|
+
Inside form templates:
|
|
338
|
+
|
|
339
|
+
```ruby
|
|
340
|
+
class Form < Form
|
|
341
|
+
def form_template
|
|
342
|
+
# Form object
|
|
343
|
+
object # The record
|
|
344
|
+
record # Alias for object
|
|
345
|
+
object.new_record? # Check if creating
|
|
346
|
+
|
|
347
|
+
# Request context
|
|
348
|
+
current_user
|
|
349
|
+
current_parent
|
|
350
|
+
request
|
|
351
|
+
params
|
|
352
|
+
|
|
353
|
+
# Definition
|
|
354
|
+
resource_definition
|
|
355
|
+
resource_fields # Permitted fields
|
|
356
|
+
|
|
357
|
+
# URL helpers
|
|
358
|
+
resource_url_for(object)
|
|
359
|
+
resource_url_for(Post, action: :new)
|
|
360
|
+
|
|
361
|
+
# Rails helpers
|
|
362
|
+
helpers.link_to(...)
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Interaction Forms
|
|
368
|
+
|
|
369
|
+
Forms for interactive actions:
|
|
370
|
+
|
|
371
|
+
```ruby
|
|
372
|
+
class PublishPostInteraction < ResourceInteraction
|
|
373
|
+
attribute :publish_date, :date
|
|
374
|
+
attribute :notify_subscribers, :boolean, default: true
|
|
375
|
+
|
|
376
|
+
input :publish_date, as: :flatpickr
|
|
377
|
+
input :notify_subscribers
|
|
378
|
+
|
|
379
|
+
# Custom form (optional)
|
|
380
|
+
class Form < Plutonium::UI::Form::Interaction
|
|
381
|
+
def form_template
|
|
382
|
+
div(class: "space-y-4") {
|
|
383
|
+
render_resource_field :publish_date
|
|
384
|
+
render_resource_field :notify_subscribers
|
|
385
|
+
}
|
|
386
|
+
render_actions
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
end
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Related
|
|
393
|
+
|
|
394
|
+
- [Fields Reference](/reference/definition/fields) - Input configuration
|
|
395
|
+
- [Views Reference](./index) - Custom page classes
|
|
396
|
+
- [Theming Guide](/guides/theming) - TailwindCSS and styling
|