plutonium 0.50.0 → 0.51.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.
Files changed (132) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +85 -102
  3. data/.claude/skills/plutonium-app/SKILL.md +572 -0
  4. data/.claude/skills/plutonium-auth/SKILL.md +163 -300
  5. data/.claude/skills/plutonium-behavior/SKILL.md +838 -0
  6. data/.claude/skills/plutonium-resource/SKILL.md +1176 -0
  7. data/.claude/skills/plutonium-tenancy/SKILL.md +655 -0
  8. data/.claude/skills/plutonium-testing/SKILL.md +6 -5
  9. data/.claude/skills/plutonium-ui/SKILL.md +900 -0
  10. data/CHANGELOG.md +27 -2
  11. data/Rakefile +2 -1
  12. data/app/assets/plutonium.css +1 -11
  13. data/app/assets/plutonium.js +1009 -1214
  14. data/app/assets/plutonium.js.map +3 -3
  15. data/app/assets/plutonium.min.js +52 -51
  16. data/app/assets/plutonium.min.js.map +3 -3
  17. data/docs/.vitepress/config.ts +37 -27
  18. data/docs/getting-started/index.md +22 -29
  19. data/docs/getting-started/installation.md +37 -80
  20. data/docs/getting-started/tutorial/index.md +4 -5
  21. data/docs/guides/adding-resources.md +66 -377
  22. data/docs/guides/authentication.md +94 -463
  23. data/docs/guides/authorization.md +124 -370
  24. data/docs/guides/creating-packages.md +94 -296
  25. data/docs/guides/custom-actions.md +121 -441
  26. data/docs/guides/index.md +22 -42
  27. data/docs/guides/multi-tenancy.md +116 -187
  28. data/docs/guides/nested-resources.md +103 -431
  29. data/docs/guides/search-filtering.md +123 -240
  30. data/docs/guides/testing.md +5 -4
  31. data/docs/guides/theming.md +157 -407
  32. data/docs/guides/troubleshooting.md +5 -3
  33. data/docs/guides/user-invites.md +106 -425
  34. data/docs/guides/user-profile.md +76 -243
  35. data/docs/index.md +1 -1
  36. data/docs/reference/app/generators.md +517 -0
  37. data/docs/reference/app/index.md +158 -0
  38. data/docs/reference/app/packages.md +146 -0
  39. data/docs/reference/app/portals.md +377 -0
  40. data/docs/reference/auth/accounts.md +230 -0
  41. data/docs/reference/auth/index.md +88 -0
  42. data/docs/reference/auth/profile.md +185 -0
  43. data/docs/reference/behavior/controllers.md +395 -0
  44. data/docs/reference/behavior/index.md +22 -0
  45. data/docs/reference/behavior/interactions.md +341 -0
  46. data/docs/reference/behavior/policies.md +417 -0
  47. data/docs/reference/index.md +56 -49
  48. data/docs/reference/resource/actions.md +423 -0
  49. data/docs/reference/resource/definition.md +508 -0
  50. data/docs/reference/resource/index.md +50 -0
  51. data/docs/reference/resource/model.md +348 -0
  52. data/docs/reference/resource/query.md +305 -0
  53. data/docs/reference/tenancy/entity-scoping.md +361 -0
  54. data/docs/reference/tenancy/index.md +36 -0
  55. data/docs/reference/tenancy/invites.md +393 -0
  56. data/docs/reference/tenancy/nested-resources.md +267 -0
  57. data/docs/reference/testing/index.md +287 -0
  58. data/docs/reference/ui/assets.md +400 -0
  59. data/docs/reference/ui/components.md +165 -0
  60. data/docs/reference/ui/displays.md +104 -0
  61. data/docs/reference/ui/forms.md +284 -0
  62. data/docs/reference/ui/index.md +30 -0
  63. data/docs/reference/ui/layouts.md +106 -0
  64. data/docs/reference/ui/pages.md +189 -0
  65. data/docs/reference/ui/tables.md +117 -0
  66. data/docs/superpowers/specs/2026-05-09-typeahead-endpoint-design.md +203 -0
  67. data/docs/superpowers/specs/2026-05-12-skill-compaction-design.md +99 -0
  68. data/docs/superpowers/specs/2026-05-13-docs-restructure-design.md +186 -0
  69. data/gemfiles/rails_7.gemfile.lock +1 -1
  70. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  71. data/gemfiles/rails_8.1.gemfile.lock +1 -1
  72. data/lib/generators/pu/core/update/update_generator.rb +0 -20
  73. data/lib/generators/pu/invites/install_generator.rb +1 -0
  74. data/lib/plutonium/definition/base.rb +1 -1
  75. data/lib/plutonium/definition/{views.rb → index_views.rb} +21 -20
  76. data/lib/plutonium/helpers/turbo_helper.rb +11 -0
  77. data/lib/plutonium/helpers/turbo_stream_actions_helper.rb +14 -0
  78. data/lib/plutonium/resource/controller.rb +1 -0
  79. data/lib/plutonium/resource/controllers/crud_actions.rb +19 -1
  80. data/lib/plutonium/resource/controllers/typeahead.rb +180 -0
  81. data/lib/plutonium/resource/policy.rb +7 -0
  82. data/lib/plutonium/routing/mapper_extensions.rb +15 -0
  83. data/lib/plutonium/ui/component/methods.rb +4 -0
  84. data/lib/plutonium/ui/form/base.rb +6 -2
  85. data/lib/plutonium/ui/form/components/json.rb +58 -0
  86. data/lib/plutonium/ui/form/components/resource_select.rb +62 -8
  87. data/lib/plutonium/ui/form/components/secure_association.rb +98 -22
  88. data/lib/plutonium/ui/form/concerns/typeahead_attributes.rb +83 -0
  89. data/lib/plutonium/ui/form/resource.rb +0 -4
  90. data/lib/plutonium/ui/grid/resource.rb +1 -1
  91. data/lib/plutonium/ui/layout/base.rb +1 -0
  92. data/lib/plutonium/ui/page/base.rb +0 -7
  93. data/lib/plutonium/ui/page/index.rb +4 -4
  94. data/lib/plutonium/ui/table/resource.rb +1 -1
  95. data/lib/plutonium/version.rb +1 -1
  96. data/lib/plutonium.rb +8 -0
  97. data/lib/tasks/release.rake +15 -1
  98. data/package.json +10 -10
  99. data/src/css/slim_select.css +4 -0
  100. data/src/js/controllers/slim_select_controller.js +61 -0
  101. data/src/js/turbo/turbo_actions.js +33 -0
  102. data/yarn.lock +553 -543
  103. metadata +44 -33
  104. data/.claude/skills/plutonium-assets/SKILL.md +0 -512
  105. data/.claude/skills/plutonium-controller/SKILL.md +0 -396
  106. data/.claude/skills/plutonium-create-resource/SKILL.md +0 -303
  107. data/.claude/skills/plutonium-definition/SKILL.md +0 -1223
  108. data/.claude/skills/plutonium-entity-scoping/SKILL.md +0 -317
  109. data/.claude/skills/plutonium-forms/SKILL.md +0 -465
  110. data/.claude/skills/plutonium-installation/SKILL.md +0 -331
  111. data/.claude/skills/plutonium-interaction/SKILL.md +0 -413
  112. data/.claude/skills/plutonium-invites/SKILL.md +0 -408
  113. data/.claude/skills/plutonium-model/SKILL.md +0 -440
  114. data/.claude/skills/plutonium-nested-resources/SKILL.md +0 -360
  115. data/.claude/skills/plutonium-package/SKILL.md +0 -198
  116. data/.claude/skills/plutonium-policy/SKILL.md +0 -456
  117. data/.claude/skills/plutonium-portal/SKILL.md +0 -410
  118. data/.claude/skills/plutonium-views/SKILL.md +0 -651
  119. data/docs/reference/assets/index.md +0 -496
  120. data/docs/reference/controller/index.md +0 -412
  121. data/docs/reference/definition/actions.md +0 -462
  122. data/docs/reference/definition/fields.md +0 -383
  123. data/docs/reference/definition/index.md +0 -326
  124. data/docs/reference/definition/query.md +0 -351
  125. data/docs/reference/generators/index.md +0 -648
  126. data/docs/reference/interaction/index.md +0 -449
  127. data/docs/reference/model/features.md +0 -248
  128. data/docs/reference/model/index.md +0 -218
  129. data/docs/reference/policy/index.md +0 -456
  130. data/docs/reference/portal/index.md +0 -379
  131. data/docs/reference/views/forms.md +0 -411
  132. data/docs/reference/views/index.md +0 -544
@@ -1,544 +0,0 @@
1
- # Views Reference
2
-
3
- Complete reference for UI customization with Phlex components.
4
-
5
- ## Overview
6
-
7
- Plutonium uses [Phlex](https://www.phlex.fun/) for all view components. Customization happens through:
8
- - Definition nested classes (pages, forms, tables, displays)
9
- - Page hooks for injecting content
10
- - Custom Phlex components
11
- - ERB view overrides
12
-
13
- ## Architecture
14
-
15
- ```
16
- Definition
17
- ├── IndexPage → renders Table
18
- ├── ShowPage → renders Display
19
- ├── NewPage → renders Form
20
- ├── EditPage → renders Form
21
- └── InteractiveActionPage → renders Interaction Form
22
- ```
23
-
24
- ## Page Configuration
25
-
26
- Set titles and descriptions in definitions:
27
-
28
- ```ruby
29
- class PostDefinition < ResourceDefinition
30
- # Page titles
31
- index_page_title "Blog Posts"
32
- index_page_description "Manage all published articles"
33
-
34
- show_page_title "Article Details"
35
- new_page_title "Write New Article"
36
- edit_page_title "Edit Article"
37
-
38
- # Breadcrumbs
39
- breadcrumbs true # Global default
40
- index_page_breadcrumbs false # Per-page override
41
- show_page_breadcrumbs true
42
- end
43
- ```
44
-
45
- ## Custom Page Classes
46
-
47
- Override page rendering by defining nested classes in your definition:
48
-
49
- ```ruby
50
- class PostDefinition < ResourceDefinition
51
- class ShowPage < ShowPage
52
- private
53
-
54
- # Custom title logic
55
- def page_title
56
- "#{object.title} - #{object.author.name}"
57
- end
58
-
59
- # Add content before the main area
60
- def render_before_content
61
- div(class: "alert alert-info") {
62
- "This post has #{object.comments.count} comments"
63
- }
64
- end
65
-
66
- # Add content after
67
- def render_after_content
68
- render RelatedPostsComponent.new(post: object)
69
- end
70
-
71
- # Override the toolbar
72
- def render_toolbar
73
- div(class: "flex gap-2") {
74
- button(class: "btn") { "Preview" }
75
- button(class: "btn btn-primary") { "Publish" }
76
- }
77
- end
78
- end
79
- end
80
- ```
81
-
82
- ## Page Hooks
83
-
84
- All pages inherit these customization hooks:
85
-
86
- | Hook | Purpose |
87
- |------|---------|
88
- | `render_before_header` | Before entire header section |
89
- | `render_after_header` | After entire header section |
90
- | `render_before_breadcrumbs` | Before breadcrumbs |
91
- | `render_after_breadcrumbs` | After breadcrumbs |
92
- | `render_before_page_header` | Before title/actions |
93
- | `render_after_page_header` | After title/actions |
94
- | `render_before_toolbar` | Before toolbar |
95
- | `render_after_toolbar` | After toolbar |
96
- | `render_before_content` | Before main content |
97
- | `render_after_content` | After main content |
98
- | `render_before_footer` | Before footer |
99
- | `render_after_footer` | After footer |
100
-
101
- ## Form Customization
102
-
103
- Override form rendering in your definition:
104
-
105
- ```ruby
106
- class PostDefinition < ResourceDefinition
107
- class Form < Form
108
- def form_template
109
- # Custom layout with sections
110
- div(class: "grid grid-cols-2 gap-6") {
111
- div {
112
- h3(class: "text-lg font-medium") { "Basic Info" }
113
- render_resource_field :title
114
- render_resource_field :slug
115
- }
116
-
117
- div {
118
- h3(class: "text-lg font-medium") { "Content" }
119
- render_resource_field :content
120
- }
121
- }
122
-
123
- div(class: "mt-6") {
124
- h3(class: "text-lg font-medium") { "Publishing" }
125
- render_resource_field :published_at
126
- render_resource_field :category
127
- }
128
-
129
- render_actions
130
- end
131
- end
132
- end
133
- ```
134
-
135
- ### Form Methods
136
-
137
- | Method | Purpose |
138
- |--------|---------|
139
- | `render_fields` | Render all permitted fields |
140
- | `render_resource_field(name)` | Render a single field |
141
- | `render_actions` | Render submit buttons |
142
- | `record` / `object` | The form object |
143
- | `resource_fields` | List of permitted field names |
144
- | `resource_definition` | The definition instance |
145
-
146
- ## Display Customization
147
-
148
- Override show page detail rendering:
149
-
150
- ```ruby
151
- class PostDefinition < ResourceDefinition
152
- class Display < Display
153
- def display_template
154
- # Hero section
155
- div(class: "bg-gradient-to-r from-blue-500 to-purple-600 p-8 rounded-lg text-white mb-6") {
156
- h1(class: "text-3xl font-bold") { object.title }
157
- p(class: "mt-2 opacity-90") { object.excerpt }
158
- }
159
-
160
- # Main content
161
- Block do
162
- fields_wrapper do
163
- render_resource_field :author
164
- render_resource_field :published_at
165
- render_resource_field :category
166
- end
167
- end
168
-
169
- # Full-width content
170
- Block do
171
- div(class: "prose max-w-none") {
172
- raw object.content
173
- }
174
- end
175
-
176
- # Associations (tabs)
177
- render_associations if present_associations?
178
- end
179
- end
180
- end
181
- ```
182
-
183
- ### Display Methods
184
-
185
- | Method | Purpose |
186
- |--------|---------|
187
- | `render_fields` | Render all permitted fields in a block |
188
- | `render_resource_field(name)` | Render single field |
189
- | `render_associations` | Render association tabs |
190
- | `object` | The record being displayed |
191
- | `resource_fields` | List of permitted field names |
192
- | `resource_associations` | List of permitted associations |
193
-
194
- ## Table Customization
195
-
196
- Override index page table:
197
-
198
- ```ruby
199
- class PostDefinition < ResourceDefinition
200
- class Table < Table
201
- def view_template
202
- render_search_bar
203
- render_scopes_bar
204
-
205
- if collection.empty?
206
- render_empty_card
207
- else
208
- # Custom card grid instead of table
209
- div(class: "grid grid-cols-3 gap-4") {
210
- collection.each do |post|
211
- render PostCardComponent.new(post:)
212
- end
213
- }
214
- end
215
-
216
- render_footer
217
- end
218
- end
219
- end
220
- ```
221
-
222
- ### Table Methods
223
-
224
- | Method | Purpose |
225
- |--------|---------|
226
- | `render_search_bar` | Search input |
227
- | `render_scopes_bar` | Scope tabs |
228
- | `render_table` | Default table |
229
- | `render_empty_card` | Empty state |
230
- | `render_footer` | Pagination |
231
- | `collection` | The paginated records |
232
- | `resource_fields` | Column field names |
233
-
234
- ## Component Kit
235
-
236
- Plutonium provides shorthand methods for common components:
237
-
238
- ```ruby
239
- class MyPage < Plutonium::UI::Page::Base
240
- def view_template
241
- # These are automatically rendered
242
- PageHeader(title: "Dashboard")
243
-
244
- Panel(class: "mt-4") {
245
- p { "Content here" }
246
- }
247
-
248
- Block {
249
- TabList(items: tabs)
250
- }
251
-
252
- EmptyCard("No items found")
253
-
254
- ActionButton(action, url: "/posts/new")
255
- end
256
- end
257
- ```
258
-
259
- Available kit methods:
260
-
261
- | Method | Purpose |
262
- |--------|---------|
263
- | `Breadcrumbs()` | Navigation breadcrumbs |
264
- | `PageHeader(title:, description:, actions:)` | Page header with actions |
265
- | `Panel(**attrs)` | Content panel |
266
- | `Block(**attrs)` | Content block |
267
- | `TabList(items:)` | Tab navigation |
268
- | `EmptyCard(message)` | Empty state card |
269
- | `ActionButton(action, url:)` | Action button |
270
- | `DynaFrameHost(src:, loading:)` | Lazy-loading turbo frame |
271
- | `DynaFrameContent(content) { \|frame\| ... }` | Frame-aware content wrapper |
272
- | `TableSearchBar()` | Search bar for tables |
273
- | `TableScopesBar()` | Scope tabs for tables |
274
- | `TableInfo(pagy)` | Pagination info |
275
- | `TablePagination(pagy)` | Pagination links |
276
- | `FrameNavigatorPanel(title:, src:, panel_id:)` | Frame navigation panel |
277
-
278
- ## DynaFrameContent Pattern
279
-
280
- `DynaFrameContent` enables frame-aware rendering. For turbo-frame requests, only the content is rendered inside the frame. For regular requests, the full page renders with header/footer.
281
-
282
- ```ruby
283
- # How Page::Base uses DynaFrameContent
284
- def view_template(&block)
285
- DynaFrameContent(page_content(block)) do |frame|
286
- render_header # Skipped for frame requests
287
- frame.render_content # Always rendered (wrapped in turbo-frame for frame requests)
288
- render_footer # Skipped for frame requests
289
- end
290
- end
291
- ```
292
-
293
- This pattern means:
294
- - **Regular page load**: Full page with header, content, footer
295
- - **Turbo-frame request**: Only `<turbo-frame id="...">` with content inside
296
-
297
- All pages automatically inherit this behavior - modals, lazy-loaded frames, and navigation all work correctly without special handling.
298
-
299
- ## Custom Components
300
-
301
- ### Creating a Phlex Component
302
-
303
- ```ruby
304
- # app/components/post_card_component.rb
305
- class PostCardComponent < Plutonium::UI::Component::Base
306
- def initialize(post:)
307
- @post = post
308
- end
309
-
310
- def view_template
311
- div(class: "bg-white rounded-lg shadow p-4") {
312
- h3(class: "font-bold") { @post.title }
313
- p(class: "text-gray-600 mt-2") { @post.excerpt }
314
-
315
- div(class: "mt-4 flex justify-between items-center") {
316
- span(class: "text-sm text-gray-500") { @post.published_at&.strftime("%B %d, %Y") }
317
- a(href: resource_url_for(@post), class: "text-blue-600") { "Read more" }
318
- }
319
- }
320
- end
321
- end
322
- ```
323
-
324
- ### Using Components in Definitions
325
-
326
- ```ruby
327
- class PostDefinition < ResourceDefinition
328
- # Custom display component
329
- display :status, as: StatusBadgeComponent
330
-
331
- # Custom input component
332
- input :color, as: ColorPickerComponent
333
-
334
- # Block with component
335
- display :metrics do |field|
336
- MetricsChartComponent.new(data: field.value)
337
- end
338
- end
339
- ```
340
-
341
- ## Page Chrome (Shell)
342
-
343
- `Plutonium.configuration.shell` controls the layout shipped above the resource pages. The default is **`:modern`** (topbar + icon rail) — leave it alone unless you're upgrading from a pre-`:modern` version and want to keep the legacy header + sidebar:
344
-
345
- ```ruby
346
- Plutonium.configure do |config|
347
- config.shell = :classic
348
- end
349
- ```
350
-
351
- To customize the shipped chrome per-portal, eject the templates:
352
-
353
- ```bash
354
- rails generate pu:eject:shell --dest=admin_portal
355
- ```
356
-
357
- This copies `_resource_header.html.erb` and `_resource_sidebar.html.erb` into the portal's `app/views/plutonium/`.
358
-
359
- ## Modal & Slideover Forms
360
-
361
- Framework-provided `:new` / `:edit` actions render inline inside a modal. Choose the chrome per-resource via [`modal`](/reference/definition/#form-configuration) on the definition (`:slideover` default, `:centered`, or `false`).
362
-
363
- Custom interactive actions render in their own dialog. Each action carries its own [`modal:` option](/reference/definition/actions#action-options) (`:centered` default, or `:slideover`).
364
-
365
- ### Detecting Render Context
366
-
367
- In custom Page / Form components:
368
-
369
- | Helper | True when |
370
- |--------|-----------|
371
- | `in_frame?` | Request is targeting a turbo-frame |
372
- | `in_modal?` | Request is rendering inside a modal/slideover |
373
-
374
- Use them to pin action strips, omit nav chrome, or swap layouts.
375
-
376
- ## Tabs & URL Hash
377
-
378
- Show pages with associations render the **Details** tab first followed by one tab per permitted association. The active tab is reflected in the URL hash (`#products`, `#refund-requests`) so the page deep-links and the active state survives reloads / back navigation. Tab rows scroll horizontally on narrow viewports rather than wrapping.
379
-
380
- ## Show Page Metadata Panel
381
-
382
- The [`metadata` DSL](/reference/definition/#show-page-metadata-panel) on the definition opts a resource into a right-side aside that renders selected fields as label/value rows alongside the main details. The aside collapses to a single-column stack below the `lg` breakpoint and disappears entirely when no listed field is permitted by policy.
383
-
384
- ## Layout Customization
385
-
386
- ### Custom Layout Class
387
-
388
- ```ruby
389
- # packages/admin_portal/app/views/layouts/admin_portal/resource_layout.rb
390
- module AdminPortal
391
- class ResourceLayout < Plutonium::UI::Layout::ResourceLayout
392
- private
393
-
394
- # Custom main content area classes
395
- def main_attributes
396
- mix(super, { class: "pt-20 lg:ml-64" })
397
- end
398
-
399
- # Add custom header content
400
- def render_before_main
401
- super
402
- render AnnouncementBanner.new if Announcement.active.any?
403
- end
404
-
405
- # Custom scripts
406
- def render_body_scripts
407
- super
408
- script(src: "/custom-analytics.js")
409
- end
410
- end
411
- end
412
- ```
413
-
414
- ### Layout Hooks
415
-
416
- | Hook | Purpose |
417
- |------|---------|
418
- | `render_before_main` | Before main content area |
419
- | `render_after_main` | After main (modals, etc.) |
420
- | `render_head` | HTML head section |
421
- | `render_title` | Page title tag |
422
- | `render_assets` | CSS/JS assets |
423
- | `render_body_scripts` | Scripts at end of body |
424
-
425
- ## Custom ERB Views
426
-
427
- For complete control, create custom ERB view files:
428
-
429
- ```
430
- # Main app (for a PostsController)
431
- app/views/posts/index.html.erb
432
- app/views/posts/show.html.erb
433
-
434
- # Portal-specific
435
- packages/admin_portal/app/views/admin_portal/posts/show.html.erb
436
- ```
437
-
438
- The default views render the page class:
439
-
440
- ```erb
441
- <%# app/views/resource/show.html.erb %>
442
- <%= render current_definition.show_page_class.new %>
443
- ```
444
-
445
- Custom view example:
446
-
447
- ```erb
448
- <%# app/views/posts/show.html.erb %>
449
- <div class="max-w-4xl mx-auto">
450
- <article class="prose lg:prose-xl">
451
- <h1><%= resource_record!.title %></h1>
452
- <%= raw resource_record!.content %>
453
- </article>
454
-
455
- <div class="mt-8">
456
- <%= link_to "Edit", resource_url_for(resource_record!, action: :edit), class: "btn" %>
457
- <%= link_to "Back", resource_url_for(Post), class: "btn" %>
458
- </div>
459
- </div>
460
- ```
461
-
462
- ## Available Context
463
-
464
- ### Resource Methods
465
-
466
- | Method | Description |
467
- |--------|-------------|
468
- | `resource_class` | The model class (e.g., `Post`) |
469
- | `resource_record!` | Current record (raises if not found) |
470
- | `resource_record?` | Current record (nil if not found) |
471
- | `current_parent` | Parent record for nested routes |
472
- | `current_scoped_entity` | Entity for multi-tenant portals |
473
-
474
- ### Definition & Policy
475
-
476
- | Method | Description |
477
- |--------|-------------|
478
- | `current_definition` | Definition instance for current resource |
479
- | `current_policy` | Policy instance for current record |
480
- | `current_authorized_scope` | Scoped collection user can access |
481
-
482
- ### URL Helpers
483
-
484
- | Method | Description |
485
- |--------|-------------|
486
- | `resource_url_for(record)` | URL for a record |
487
- | `resource_url_for(record, action: :edit)` | Action URL for record |
488
- | `resource_url_for(Model)` | Index URL for model |
489
- | `resource_url_for(Model, action: :new)` | New URL for model |
490
- | `resource_url_for(record, parent: parent)` | Nested resource URL |
491
-
492
- ### Display Helpers
493
-
494
- | Method | Description |
495
- |--------|-------------|
496
- | `display_name_of(record)` | Human-readable name for record |
497
- | `resource_name(klass)` | Singular model name |
498
- | `resource_name_plural(klass)` | Plural model name |
499
-
500
- ### In Phlex Components
501
-
502
- ```ruby
503
- class MyComponent < Plutonium::UI::Component::Base
504
- def view_template
505
- # Plutonium methods work directly
506
- current_user
507
- resource_record!
508
- resource_url_for(@post)
509
-
510
- # Rails helpers via helpers proxy
511
- helpers.link_to(...)
512
- helpers.image_tag(...)
513
- end
514
- end
515
- ```
516
-
517
- ## Portal-Specific Views
518
-
519
- Each portal can override views:
520
-
521
- ```ruby
522
- # Base definition
523
- class PostDefinition < ResourceDefinition
524
- class ShowPage < ShowPage
525
- # Default behavior
526
- end
527
- end
528
-
529
- # Admin portal override
530
- class AdminPortal::PostDefinition < ::PostDefinition
531
- class ShowPage < ShowPage # Inherits from ::PostDefinition::ShowPage
532
- def render_after_content
533
- super
534
- render AdminOnlySection.new(post: object)
535
- end
536
- end
537
- end
538
- ```
539
-
540
- ## Related
541
-
542
- - [Forms Reference](./forms) - Custom form templates and field builders
543
- - [Theming Guide](/guides/theming) - TailwindCSS and styling
544
- - [Fields Reference](/reference/definition/fields) - Field configuration