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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/# Plutonium: The pre-alpha demo.md +4 -2
  3. data/.claude/skills/assets/SKILL.md +416 -0
  4. data/.claude/skills/connect-resource/SKILL.md +112 -0
  5. data/.claude/skills/controller/SKILL.md +302 -0
  6. data/.claude/skills/create-resource/SKILL.md +240 -0
  7. data/.claude/skills/definition/SKILL.md +218 -0
  8. data/.claude/skills/definition-actions/SKILL.md +386 -0
  9. data/.claude/skills/definition-fields/SKILL.md +474 -0
  10. data/.claude/skills/definition-query/SKILL.md +334 -0
  11. data/.claude/skills/forms/SKILL.md +439 -0
  12. data/.claude/skills/installation/SKILL.md +300 -0
  13. data/.claude/skills/interaction/SKILL.md +382 -0
  14. data/.claude/skills/model/SKILL.md +267 -0
  15. data/.claude/skills/model-features/SKILL.md +286 -0
  16. data/.claude/skills/nested-resources/SKILL.md +274 -0
  17. data/.claude/skills/package/SKILL.md +191 -0
  18. data/.claude/skills/policy/SKILL.md +352 -0
  19. data/.claude/skills/portal/SKILL.md +400 -0
  20. data/.claude/skills/resource/SKILL.md +281 -0
  21. data/.claude/skills/rodauth/SKILL.md +452 -0
  22. data/.claude/skills/views/SKILL.md +563 -0
  23. data/Appraisals +46 -4
  24. data/CHANGELOG.md +32 -1
  25. data/app/assets/plutonium.css +2 -2
  26. data/config/brakeman.ignore +239 -0
  27. data/config/initializers/action_policy.rb +1 -1
  28. data/docs/.vitepress/config.ts +132 -47
  29. data/docs/concepts/architecture.md +226 -0
  30. data/docs/concepts/auto-detection.md +254 -0
  31. data/docs/concepts/index.md +61 -0
  32. data/docs/concepts/packages-portals.md +304 -0
  33. data/docs/concepts/resources.md +224 -0
  34. data/docs/cookbook/blog.md +412 -0
  35. data/docs/cookbook/index.md +289 -0
  36. data/docs/cookbook/saas.md +481 -0
  37. data/docs/getting-started/index.md +56 -0
  38. data/docs/getting-started/installation.md +146 -0
  39. data/docs/getting-started/tutorial/01-setup.md +118 -0
  40. data/docs/getting-started/tutorial/02-first-resource.md +180 -0
  41. data/docs/getting-started/tutorial/03-authentication.md +246 -0
  42. data/docs/getting-started/tutorial/04-authorization.md +170 -0
  43. data/docs/getting-started/tutorial/05-custom-actions.md +202 -0
  44. data/docs/getting-started/tutorial/06-nested-resources.md +147 -0
  45. data/docs/getting-started/tutorial/07-customizing-ui.md +254 -0
  46. data/docs/getting-started/tutorial/index.md +64 -0
  47. data/docs/guides/adding-resources.md +420 -0
  48. data/docs/guides/authentication.md +551 -0
  49. data/docs/guides/authorization.md +468 -0
  50. data/docs/guides/creating-packages.md +380 -0
  51. data/docs/guides/custom-actions.md +523 -0
  52. data/docs/guides/index.md +45 -0
  53. data/docs/guides/multi-tenancy.md +302 -0
  54. data/docs/guides/nested-resources.md +411 -0
  55. data/docs/guides/search-filtering.md +266 -0
  56. data/docs/guides/theming.md +321 -0
  57. data/docs/index.md +67 -26
  58. data/docs/public/CLAUDE.md +64 -21
  59. data/docs/reference/assets/index.md +496 -0
  60. data/docs/reference/controller/index.md +363 -0
  61. data/docs/reference/definition/actions.md +400 -0
  62. data/docs/reference/definition/fields.md +350 -0
  63. data/docs/reference/definition/index.md +252 -0
  64. data/docs/reference/definition/query.md +342 -0
  65. data/docs/reference/generators/index.md +469 -0
  66. data/docs/reference/index.md +49 -0
  67. data/docs/reference/interaction/index.md +445 -0
  68. data/docs/reference/model/features.md +248 -0
  69. data/docs/reference/model/index.md +219 -0
  70. data/docs/reference/policy/index.md +385 -0
  71. data/docs/reference/portal/index.md +382 -0
  72. data/docs/reference/views/forms.md +396 -0
  73. data/docs/reference/views/index.md +479 -0
  74. data/gemfiles/rails_7.gemfile +9 -2
  75. data/gemfiles/rails_7.gemfile.lock +146 -111
  76. data/gemfiles/rails_8.0.gemfile +20 -0
  77. data/gemfiles/rails_8.0.gemfile.lock +417 -0
  78. data/gemfiles/rails_8.1.gemfile +20 -0
  79. data/gemfiles/rails_8.1.gemfile.lock +419 -0
  80. data/lib/generators/pu/gem/dotenv/templates/.env +2 -0
  81. data/lib/generators/pu/gem/dotenv/templates/config/initializers/001_ensure_required_env.rb +3 -1
  82. data/lib/generators/pu/lib/plutonium_generators/model_generator_base.rb +13 -16
  83. data/lib/generators/pu/pkg/portal/USAGE +65 -0
  84. data/lib/generators/pu/pkg/portal/portal_generator.rb +22 -9
  85. data/lib/generators/pu/res/conn/USAGE +71 -0
  86. data/lib/generators/pu/res/model/USAGE +106 -110
  87. data/lib/generators/pu/res/model/templates/model.rb.tt +6 -2
  88. data/lib/generators/pu/res/scaffold/USAGE +85 -0
  89. data/lib/generators/pu/rodauth/install_generator.rb +2 -6
  90. data/lib/generators/pu/rodauth/templates/config/initializers/url_options.rb +17 -0
  91. data/lib/generators/pu/skills/sync/USAGE +14 -0
  92. data/lib/generators/pu/skills/sync/sync_generator.rb +66 -0
  93. data/lib/plutonium/action_policy/sti_policy_lookup.rb +1 -1
  94. data/lib/plutonium/core/controller.rb +2 -2
  95. data/lib/plutonium/interaction/base.rb +1 -0
  96. data/lib/plutonium/package/engine.rb +2 -2
  97. data/lib/plutonium/query/adhoc_block.rb +6 -2
  98. data/lib/plutonium/query/model_scope.rb +1 -1
  99. data/lib/plutonium/railtie.rb +4 -0
  100. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +1 -1
  101. data/lib/plutonium/resource/query_object.rb +38 -8
  102. data/lib/plutonium/ui/table/components/scopes_bar.rb +39 -34
  103. data/lib/plutonium/version.rb +1 -1
  104. data/lib/tasks/release.rake +19 -4
  105. data/package.json +1 -1
  106. metadata +76 -39
  107. data/brakeman.ignore +0 -28
  108. data/docs/api-examples.md +0 -49
  109. data/docs/guide/claude-code-guide.md +0 -74
  110. data/docs/guide/deep-dive/authorization.md +0 -189
  111. data/docs/guide/deep-dive/multitenancy.md +0 -256
  112. data/docs/guide/deep-dive/resources.md +0 -390
  113. data/docs/guide/getting-started/01-installation.md +0 -165
  114. data/docs/guide/index.md +0 -28
  115. data/docs/guide/introduction/01-what-is-plutonium.md +0 -211
  116. data/docs/guide/introduction/02-core-concepts.md +0 -440
  117. data/docs/guide/tutorial/01-project-setup.md +0 -75
  118. data/docs/guide/tutorial/02-creating-a-feature-package.md +0 -45
  119. data/docs/guide/tutorial/03-defining-resources.md +0 -90
  120. data/docs/guide/tutorial/04-creating-a-portal.md +0 -101
  121. data/docs/guide/tutorial/05-customizing-the-ui.md +0 -128
  122. data/docs/guide/tutorial/06-adding-custom-actions.md +0 -101
  123. data/docs/guide/tutorial/07-implementing-authorization.md +0 -90
  124. data/docs/markdown-examples.md +0 -85
  125. data/docs/modules/action.md +0 -244
  126. data/docs/modules/authentication.md +0 -236
  127. data/docs/modules/configuration.md +0 -599
  128. data/docs/modules/controller.md +0 -443
  129. data/docs/modules/core.md +0 -316
  130. data/docs/modules/definition.md +0 -1308
  131. data/docs/modules/display.md +0 -759
  132. data/docs/modules/form.md +0 -495
  133. data/docs/modules/generator.md +0 -400
  134. data/docs/modules/index.md +0 -167
  135. data/docs/modules/interaction.md +0 -642
  136. data/docs/modules/package.md +0 -151
  137. data/docs/modules/policy.md +0 -176
  138. data/docs/modules/portal.md +0 -710
  139. data/docs/modules/query.md +0 -297
  140. data/docs/modules/resource_record.md +0 -618
  141. data/docs/modules/routing.md +0 -690
  142. data/docs/modules/table.md +0 -301
  143. data/docs/modules/ui.md +0 -631
@@ -0,0 +1,523 @@
1
+ # Custom Actions
2
+
3
+ This guide covers adding custom actions beyond standard CRUD operations.
4
+
5
+ ## Overview
6
+
7
+ Custom actions let you add buttons like "Publish", "Archive", or "Send Invoice" to your resources. Actions can be:
8
+
9
+ - **Simple** - Navigation to another page
10
+ - **Interactive** - Execute business logic with optional user input
11
+
12
+ ## Action Types
13
+
14
+ | Type | Shows In | Use Case |
15
+ |------|----------|----------|
16
+ | `resource_action` | Index page | Import, Export, Create new |
17
+ | `record_action` | Show page | Edit, Delete, Archive |
18
+ | `collection_record_action` | Table rows | Quick actions per row |
19
+ | `bulk_action` | Selected records | Bulk operations |
20
+
21
+ ## Simple Actions (Navigation)
22
+
23
+ For actions that just navigate somewhere (the target route must already exist):
24
+
25
+ ```ruby
26
+ class PostDefinition < ResourceDefinition
27
+ # Link to external URL
28
+ action :documentation,
29
+ label: "Documentation",
30
+ route_options: {url: "https://docs.example.com"},
31
+ icon: Phlex::TablerIcons::Book,
32
+ resource_action: true
33
+
34
+ # Link to custom controller action
35
+ action :reports,
36
+ route_options: {action: :reports},
37
+ icon: Phlex::TablerIcons::ChartBar,
38
+ resource_action: true
39
+ end
40
+ ```
41
+
42
+ **Note:** For custom operations with business logic, use Interactive Actions with an Interaction class.
43
+
44
+ ## Interactive Actions with Interactions
45
+
46
+ For actions that execute business logic, use Interactions.
47
+
48
+ ### Creating an Interaction
49
+
50
+ ```ruby
51
+ # app/interactions/resource_interaction.rb (generated during install)
52
+ class ResourceInteraction < Plutonium::Resource::Interaction
53
+ end
54
+
55
+ # app/interactions/publish_post_interaction.rb
56
+ class PublishPostInteraction < ResourceInteraction
57
+ # UI configuration
58
+ presents label: "Publish Post",
59
+ icon: Phlex::TablerIcons::Send,
60
+ description: "Make this post public"
61
+
62
+ # The record being acted on
63
+ attribute :resource
64
+
65
+ # Validation
66
+ validate :not_already_published
67
+
68
+ private
69
+
70
+ # Main logic
71
+ def execute
72
+ resource.update!(
73
+ published: true,
74
+ published_at: Time.current
75
+ )
76
+
77
+ succeed(resource)
78
+ .with_message("Post published successfully!")
79
+ rescue ActiveRecord::RecordInvalid => e
80
+ failed(e.record.errors)
81
+ end
82
+
83
+ def not_already_published
84
+ if resource.published?
85
+ errors.add(:base, "Post is already published")
86
+ end
87
+ end
88
+ end
89
+ ```
90
+
91
+ ### Registering the Action
92
+
93
+ ```ruby
94
+ class PostDefinition < ResourceDefinition
95
+ action :publish, interaction: PublishPostInteraction
96
+ end
97
+ ```
98
+
99
+ ### Authorizing the Action
100
+
101
+ ```ruby
102
+ class PostPolicy < ResourcePolicy
103
+ def publish?
104
+ update? && !record.published?
105
+ end
106
+ end
107
+ ```
108
+
109
+ ## Actions with User Input
110
+
111
+ Interactions can accept user input via attributes:
112
+
113
+ ```ruby
114
+ class SchedulePostInteraction < ResourceInteraction
115
+ presents label: "Schedule Publication",
116
+ icon: Phlex::TablerIcons::Calendar
117
+
118
+ # The record
119
+ attribute :resource
120
+
121
+ # User inputs
122
+ attribute :publish_at, :datetime
123
+ attribute :notify_subscribers, :boolean, default: true
124
+
125
+ # Configure form inputs
126
+ input :publish_at, as: :datetime
127
+ input :notify_subscribers, as: :boolean
128
+
129
+ # Validations
130
+ validates :publish_at, presence: true
131
+ validate :publish_at_in_future
132
+
133
+ private
134
+
135
+ def execute
136
+ resource.update!(
137
+ scheduled_at: publish_at,
138
+ notify_on_publish: notify_subscribers
139
+ )
140
+
141
+ SchedulePublicationJob.perform_at(publish_at, resource.id)
142
+
143
+ succeed(resource)
144
+ .with_message("Post scheduled for #{publish_at.strftime('%B %d at %I:%M %p')}")
145
+ rescue ActiveRecord::RecordInvalid => e
146
+ failed(e.record.errors)
147
+ end
148
+
149
+ def publish_at_in_future
150
+ if publish_at.present? && publish_at <= Time.current
151
+ errors.add(:publish_at, "must be in the future")
152
+ end
153
+ end
154
+ end
155
+ ```
156
+
157
+ Register with the definition:
158
+
159
+ ```ruby
160
+ action :schedule, interaction: SchedulePostInteraction
161
+ ```
162
+
163
+ Now users see a form with date picker and checkbox before execution.
164
+
165
+ ## Immediate vs Form Actions
166
+
167
+ Plutonium automatically determines if an action needs a form:
168
+
169
+ - **Has inputs defined** → Shows form first
170
+ - **No inputs** → Executes immediately (with optional confirmation)
171
+
172
+ ```ruby
173
+ # Shows form (has inputs)
174
+ class InviteUserInteraction < ResourceInteraction
175
+ attribute :resource
176
+ attribute :email
177
+ input :email # This triggers form display
178
+ end
179
+
180
+ # Immediate execution (no inputs)
181
+ class ArchiveInteraction < ResourceInteraction
182
+ attribute :resource
183
+ # No inputs = immediate with confirmation
184
+ end
185
+ ```
186
+
187
+ ## Action Visibility
188
+
189
+ Control where actions appear:
190
+
191
+ ```ruby
192
+ action :publish,
193
+ interaction: PublishPostInteraction,
194
+ record_action: true, # Show on show page
195
+ collection_record_action: true # Show in table rows
196
+ ```
197
+
198
+ ### Record Actions (Single Records)
199
+
200
+ ```ruby
201
+ action :publish, interaction: PublishPostInteraction
202
+ action :archive, interaction: ArchiveInteraction, record_action: true
203
+ ```
204
+
205
+ ### Bulk Actions (Multiple Records)
206
+
207
+ ```ruby
208
+ action :bulk_publish, interaction: BulkPublishInteraction
209
+ action :bulk_archive, interaction: BulkArchiveInteraction
210
+ ```
211
+
212
+ ### Resource Actions (No Record)
213
+
214
+ ```ruby
215
+ action :import, interaction: ImportInteraction, resource_action: true
216
+ action :export, interaction: ExportInteraction, resource_action: true
217
+ ```
218
+
219
+ ## Bulk Action Interaction
220
+
221
+ ```ruby
222
+ class BulkPublishInteraction < ResourceInteraction
223
+ presents label: "Publish Selected",
224
+ icon: Phlex::TablerIcons::Send
225
+
226
+ # Note: plural 'resources' for bulk actions
227
+ attribute :resources
228
+
229
+ private
230
+
231
+ def execute
232
+ count = resources.update_all(
233
+ published: true,
234
+ published_at: Time.current
235
+ )
236
+
237
+ succeed(resources)
238
+ .with_message("#{count} posts published")
239
+ end
240
+ end
241
+ ```
242
+
243
+ ## Resource Action (No Record)
244
+
245
+ ```ruby
246
+ class ImportInteraction < ResourceInteraction
247
+ presents label: "Import CSV",
248
+ icon: Phlex::TablerIcons::Upload
249
+
250
+ # No :resource or :resources = resource action
251
+ attribute :file
252
+
253
+ input :file, as: :file
254
+
255
+ validates :file, presence: true
256
+
257
+ private
258
+
259
+ def execute
260
+ # Import logic...
261
+ succeed(nil).with_message("Import completed.")
262
+ end
263
+ end
264
+ ```
265
+
266
+ ## Action Options
267
+
268
+ ```ruby
269
+ action :name,
270
+ interaction: MyInteraction,
271
+
272
+ # Display
273
+ label: "Custom Label", # Override interaction label
274
+ icon: Phlex::TablerIcons::Star, # Override interaction icon
275
+ color: :danger, # :primary, :secondary, :danger
276
+
277
+ # Visibility
278
+ resource_action: true, # Show on index page
279
+ record_action: true, # Show on show page
280
+ collection_record_action: true, # Show in table rows
281
+ bulk_action: true, # For selected records
282
+
283
+ # Grouping
284
+ category: :danger, # :primary, :secondary, :danger
285
+ position: 50, # Order (lower = first)
286
+
287
+ # Behavior
288
+ confirmation: "Are you sure?", # Confirmation dialog
289
+ turbo_frame: "_top" # Turbo frame target
290
+ ```
291
+
292
+ ## Confirmation Dialogs
293
+
294
+ Require confirmation before executing:
295
+
296
+ ```ruby
297
+ action :delete,
298
+ interaction: DeleteInteraction,
299
+ confirmation: "Are you sure you want to delete this post?"
300
+
301
+ action :bulk_delete,
302
+ interaction: BulkDeleteInteraction,
303
+ confirmation: "Delete all selected posts? This cannot be undone."
304
+ ```
305
+
306
+ ## Interaction Outcomes
307
+
308
+ ### Success
309
+
310
+ ```ruby
311
+ def execute
312
+ # ... do work ...
313
+
314
+ # Basic success
315
+ succeed(resource)
316
+
317
+ # With message
318
+ succeed(resource).with_message("Success!")
319
+
320
+ # With redirect
321
+ succeed(resource)
322
+ .with_redirect_response(posts_path)
323
+ .with_message("Post created!")
324
+
325
+ # With file download
326
+ succeed(resource)
327
+ .with_file_response(pdf_path, filename: "invoice.pdf")
328
+ end
329
+ ```
330
+
331
+ ### Failure
332
+
333
+ ```ruby
334
+ def execute
335
+ # From ActiveModel errors
336
+ failed(resource.errors)
337
+
338
+ # With custom message
339
+ failed("Something went wrong")
340
+
341
+ # With specific field
342
+ failed("is invalid", :email)
343
+
344
+ # With hash of errors
345
+ failed(email: "is invalid", name: "is required")
346
+ end
347
+ ```
348
+
349
+ ### Chaining Interactions
350
+
351
+ ```ruby
352
+ def execute
353
+ CreateUserInteraction.call(view_context:, **user_params)
354
+ .and_then { |result| SendWelcomeEmail.call(view_context:, user: result.value) }
355
+ .and_then { |result| LogActivity.call(view_context:, user: result.value) }
356
+ .with_message("User created and welcomed!")
357
+ end
358
+ ```
359
+
360
+ On failure, the chain short-circuits and returns the failure immediately.
361
+
362
+ ## Accessing Context
363
+
364
+ Interactions have access to `current_user` and `view_context`:
365
+
366
+ ```ruby
367
+ class PublishPostInteraction < ResourceInteraction
368
+ attribute :resource
369
+
370
+ private
371
+
372
+ def execute
373
+ resource.update!(
374
+ published: true,
375
+ published_by: current_user # Built-in helper
376
+ )
377
+
378
+ succeed(resource)
379
+ end
380
+ end
381
+ ```
382
+
383
+ For advanced access:
384
+
385
+ ```ruby
386
+ def execute
387
+ # Access helpers via view_context
388
+ view_context.controller.helpers.some_helper
389
+
390
+ # Access params
391
+ view_context.params
392
+
393
+ succeed(resource)
394
+ end
395
+ ```
396
+
397
+ ## Complete Example: Send Invoice
398
+
399
+ ```ruby
400
+ class SendInvoiceInteraction < ResourceInteraction
401
+ presents label: "Send Invoice",
402
+ icon: Phlex::TablerIcons::Mail,
403
+ description: "Email invoice to recipient"
404
+
405
+ attribute :resource # The invoice
406
+ attribute :recipient_email, :string
407
+ attribute :message, :text
408
+ attribute :attach_pdf, :boolean, default: true
409
+
410
+ input :recipient_email, as: :email, hint: "Recipient's email address"
411
+ input :message, as: :text, hint: "Optional message to include"
412
+ input :attach_pdf, as: :boolean
413
+
414
+ validates :recipient_email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
415
+
416
+ private
417
+
418
+ def execute
419
+ # Generate PDF if requested
420
+ pdf = attach_pdf ? generate_pdf : nil
421
+
422
+ # Send email
423
+ InvoiceMailer.send_invoice(
424
+ invoice: resource,
425
+ to: recipient_email,
426
+ message: message,
427
+ attachment: pdf
428
+ ).deliver_later
429
+
430
+ # Update invoice status
431
+ resource.update!(
432
+ sent_at: Time.current,
433
+ sent_to: recipient_email
434
+ )
435
+
436
+ succeed(resource)
437
+ .with_message("Invoice sent to #{recipient_email}")
438
+ rescue ActiveRecord::RecordInvalid => e
439
+ failed(e.record.errors)
440
+ end
441
+
442
+ def generate_pdf
443
+ InvoicePdfGenerator.new(resource).generate
444
+ end
445
+ end
446
+ ```
447
+
448
+ ## Inherited Actions
449
+
450
+ Define common actions in your base definition:
451
+
452
+ ```ruby
453
+ # app/definitions/resource_definition.rb
454
+ class ResourceDefinition < Plutonium::Resource::Definition
455
+ action :archive,
456
+ interaction: ArchiveInteraction,
457
+ color: :danger,
458
+ position: 1000
459
+ end
460
+
461
+ # All definitions inherit the archive action
462
+ class PostDefinition < ResourceDefinition
463
+ # Already has :archive action
464
+ end
465
+ ```
466
+
467
+ ## Portal-Specific Actions
468
+
469
+ Override or add actions for specific portals:
470
+
471
+ ```ruby
472
+ # packages/admin_portal/app/definitions/admin_portal/post_definition.rb
473
+ class AdminPortal::PostDefinition < ::PostDefinition
474
+ # Add admin-only actions
475
+ action :feature, interaction: FeaturePostInteraction
476
+ action :bulk_publish, interaction: BulkPublishInteraction
477
+ end
478
+ ```
479
+
480
+ ## Testing Interactions
481
+
482
+ ```ruby
483
+ RSpec.describe PublishPostInteraction do
484
+ let(:user) { create(:user) }
485
+ let(:post) { create(:post, user: user, published: false) }
486
+ let(:view_context) { double(controller: double(helpers: double(current_user: user))) }
487
+
488
+ subject { described_class.new(view_context: view_context, resource: post) }
489
+
490
+ describe '#call' do
491
+ it 'publishes the post' do
492
+ result = subject.call
493
+
494
+ expect(result).to be_success
495
+ expect(post.reload.published?).to be true
496
+ end
497
+
498
+ context 'when already published' do
499
+ before { post.update!(published: true) }
500
+
501
+ it 'fails with error' do
502
+ result = subject.call
503
+
504
+ expect(result).to be_failure
505
+ expect(subject.errors[:base]).to include("Post is already published")
506
+ end
507
+ end
508
+ end
509
+ end
510
+ ```
511
+
512
+ ## Best Practices
513
+
514
+ 1. **Keep interactions focused** - One action per interaction
515
+ 2. **Use validations** - Validate all inputs before execution
516
+ 3. **Handle errors gracefully** - Rescue exceptions and return `failed()`
517
+ 4. **Return meaningful messages** - Help users understand what happened
518
+ 5. **Use `and_then` for chains** - Compose complex workflows from simple interactions
519
+
520
+ ## Related
521
+
522
+ - [Authorization](./authorization)
523
+ - [Adding Resources](./adding-resources)
@@ -0,0 +1,45 @@
1
+ # Guides
2
+
3
+ Task-oriented guides for common Plutonium operations.
4
+
5
+ ## Getting Things Done
6
+
7
+ These guides show you how to accomplish specific tasks, with complete examples.
8
+
9
+ ### Setup & Resources
10
+
11
+ - [Adding Resources](./adding-resources) - Create and connect resources to portals
12
+ - [Creating Packages](./creating-packages) - Organize code into feature and portal packages
13
+
14
+ ### Authentication & Authorization
15
+
16
+ - [Authentication](./authentication) - Set up user authentication with Rodauth
17
+ - [Authorization](./authorization) - Implement policies for access control
18
+
19
+ ### Features
20
+
21
+ - [Custom Actions](./custom-actions) - Add interactive actions with Interactions
22
+ - [Nested Resources](./nested-resources) - Parent/child resource relationships
23
+ - [Multi-tenancy](./multi-tenancy) - Scope data to organizations or accounts
24
+ - [Search and Filtering](./search-filtering) - Implement search, filters, and scopes
25
+
26
+ ### Customization
27
+
28
+ - [Theming](./theming) - Customize colors, styles, and branding
29
+
30
+ ## Finding What You Need
31
+
32
+ | I want to... | Guide |
33
+ |--------------|-------|
34
+ | Add a new model to my app | [Adding Resources](./adding-resources) |
35
+ | Protect pages with login | [Authentication](./authentication) |
36
+ | Control who can edit what | [Authorization](./authorization) |
37
+ | Add a "Publish" button | [Custom Actions](./custom-actions) |
38
+ | Show comments under posts | [Nested Resources](./nested-resources) |
39
+ | Separate data by company | [Multi-tenancy](./multi-tenancy) |
40
+ | Add search to a list | [Search and Filtering](./search-filtering) |
41
+ | Change the color scheme | [Theming](./theming) |
42
+
43
+ ## Looking for Reference Docs?
44
+
45
+ For complete API documentation, see the [Reference](/reference/) section.