plutonium 0.56.0 → 0.56.2
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/.claude/skills/plutonium/SKILL.md +1 -1
- data/.claude/skills/plutonium-app/SKILL.md +1 -1
- data/.claude/skills/plutonium-auth/SKILL.md +1 -1
- data/.claude/skills/plutonium-resource/SKILL.md +35 -1
- data/CHANGELOG.md +19 -0
- data/CONTRIBUTING.md +1 -1
- data/README.md +38 -16
- data/app/assets/plutonium.css +1 -1
- data/docs/getting-started/installation.md +2 -2
- data/docs/getting-started/tutorial/02-first-resource.md +1 -1
- data/docs/getting-started/tutorial/03-authentication.md +3 -3
- data/docs/guides/adding-resources.md +1 -1
- data/docs/guides/authentication.md +1 -1
- data/docs/guides/creating-packages.md +1 -1
- data/docs/guides/multi-tenancy.md +1 -1
- data/docs/guides/nested-resources.md +1 -1
- data/docs/guides/user-invites.md +1 -1
- data/docs/guides/user-profile.md +1 -1
- data/docs/reference/app/generators.md +3 -3
- data/docs/reference/app/index.md +1 -1
- data/docs/reference/app/portals.md +1 -1
- data/docs/reference/auth/profile.md +1 -1
- data/docs/reference/resource/actions.md +55 -0
- data/docs/reference/resource/index.md +1 -1
- data/docs/reference/tenancy/invites.md +1 -1
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile.lock +1 -1
- data/lib/generators/pu/core/typespec/typespec_generator.rb +1 -1
- data/lib/generators/pu/core/update/update_generator.rb +10 -1
- data/lib/generators/pu/saas/welcome_generator.rb +1 -1
- data/lib/plutonium/action/base.rb +19 -2
- data/lib/plutonium/action/condition_context.rb +33 -0
- data/lib/plutonium/ui/grid/card.rb +3 -2
- data/lib/plutonium/ui/page/index.rb +1 -1
- data/lib/plutonium/ui/page/show.rb +1 -1
- data/lib/plutonium/ui/table/components/pagy_pagination.rb +3 -3
- data/lib/plutonium/ui/table/resource.rb +2 -2
- data/lib/plutonium/version.rb +1 -1
- data/package.json +1 -1
- metadata +2 -1
|
@@ -15,7 +15,7 @@ After the template completes:
|
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
cd myapp
|
|
18
|
-
rails db:
|
|
18
|
+
rails db:prepare
|
|
19
19
|
bin/dev
|
|
20
20
|
```
|
|
21
21
|
|
|
@@ -51,7 +51,7 @@ rails generate pu:core:install
|
|
|
51
51
|
```bash
|
|
52
52
|
rails generate pu:rodauth:install
|
|
53
53
|
rails generate pu:rodauth:account user
|
|
54
|
-
rails db:
|
|
54
|
+
rails db:prepare
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
For account options and customization, see [Reference › Auth](/reference/auth/) and [Guides › Authentication](/guides/authentication).
|
|
@@ -28,7 +28,7 @@ Plutonium supports multiple account types. For admins, use the dedicated `pu:rod
|
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
rails generate pu:rodauth:admin admin
|
|
31
|
-
rails db:
|
|
31
|
+
rails db:prepare
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
For self-service user accounts, the corresponding command is `rails generate pu:rodauth:account user`.
|
|
@@ -151,7 +151,7 @@ Our blog needs users (authors) who can create posts. Let's create a User account
|
|
|
151
151
|
|
|
152
152
|
```bash
|
|
153
153
|
rails generate pu:rodauth:account user
|
|
154
|
-
rails db:
|
|
154
|
+
rails db:prepare
|
|
155
155
|
```
|
|
156
156
|
|
|
157
157
|
This creates a `User` model similar to `Admin`, but with public registration enabled.
|
|
@@ -177,7 +177,7 @@ end
|
|
|
177
177
|
Run the migration:
|
|
178
178
|
|
|
179
179
|
```bash
|
|
180
|
-
rails db:
|
|
180
|
+
rails db:prepare
|
|
181
181
|
```
|
|
182
182
|
|
|
183
183
|
Update the Post model to include the association:
|
|
@@ -42,7 +42,7 @@ rails g pu:res:scaffold Organization name:string:uniq slug:string:uniq --dest=ma
|
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
44
|
rails g pu:res:scaffold Post organization:belongs_to title:string content:text --dest=main_app
|
|
45
|
-
rails db:
|
|
45
|
+
rails db:prepare
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
### 3. Scope the portal to the entity
|
|
@@ -20,7 +20,7 @@ All of this happens with no manual route wiring — Plutonium generates it from
|
|
|
20
20
|
```bash
|
|
21
21
|
rails g pu:res:scaffold Company name:string --dest=main_app
|
|
22
22
|
rails g pu:res:scaffold Property company:belongs_to name:string --dest=main_app
|
|
23
|
-
rails db:
|
|
23
|
+
rails db:prepare
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
### 2. Connect both to the portal
|
data/docs/guides/user-invites.md
CHANGED
data/docs/guides/user-profile.md
CHANGED
|
@@ -455,7 +455,7 @@ rails generate pu:pkg:portal admin --auth=admin
|
|
|
455
455
|
rails generate pu:res:conn Post Comment --dest=admin_portal
|
|
456
456
|
|
|
457
457
|
# 6. Migrate
|
|
458
|
-
rails db:
|
|
458
|
+
rails db:prepare
|
|
459
459
|
|
|
460
460
|
# 7. Create the first admin
|
|
461
461
|
rails rodauth_admin:create[admin@example.com,password123]
|
|
@@ -465,7 +465,7 @@ rails rodauth_admin:create[admin@example.com,password123]
|
|
|
465
465
|
|
|
466
466
|
```bash
|
|
467
467
|
rails g pu:res:scaffold Product name:string price_cents:integer --dest=main_app
|
|
468
|
-
rails db:
|
|
468
|
+
rails db:prepare
|
|
469
469
|
rails g pu:res:conn Product --dest=admin_portal
|
|
470
470
|
```
|
|
471
471
|
|
|
@@ -474,7 +474,7 @@ rails g pu:res:conn Product --dest=admin_portal
|
|
|
474
474
|
```bash
|
|
475
475
|
rails g pu:pkg:portal customer --auth=user --scope=Organization
|
|
476
476
|
rails g pu:res:conn Order --dest=customer_portal
|
|
477
|
-
rails db:
|
|
477
|
+
rails db:prepare
|
|
478
478
|
```
|
|
479
479
|
|
|
480
480
|
## Undoing generators
|
data/docs/reference/app/index.md
CHANGED
|
@@ -50,7 +50,7 @@ rails generate pu:pkg:portal admin --auth=user
|
|
|
50
50
|
|
|
51
51
|
# 4. First resource
|
|
52
52
|
rails generate pu:res:scaffold Post user:belongs_to title:string 'content:text?' --dest=main_app
|
|
53
|
-
rails db:
|
|
53
|
+
rails db:prepare
|
|
54
54
|
|
|
55
55
|
# 5. Connect resource to portal
|
|
56
56
|
rails generate pu:res:conn Post --dest=admin_portal
|
|
@@ -195,7 +195,7 @@ rails g pu:res:conn Profile --dest=customer_portal --singular
|
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
::: tip Run after migrations
|
|
198
|
-
The generator reads model columns to seed the policy's `permitted_attributes_for_*`. Run `rails db:
|
|
198
|
+
The generator reads model columns to seed the policy's `permitted_attributes_for_*`. Run `rails db:prepare` first.
|
|
199
199
|
:::
|
|
200
200
|
|
|
201
201
|
### What gets generated
|
|
@@ -24,7 +24,7 @@ Meta-generator: runs `pu:profile:install` + `pu:profile:conn` in one shot.
|
|
|
24
24
|
rails generate pu:profile:install bio:text avatar:attachment 'timezone:string?' \
|
|
25
25
|
--dest=customer
|
|
26
26
|
|
|
27
|
-
rails db:
|
|
27
|
+
rails db:prepare
|
|
28
28
|
|
|
29
29
|
rails generate pu:profile:conn --dest=customer_portal
|
|
30
30
|
```
|
|
@@ -61,6 +61,9 @@ action :name,
|
|
|
61
61
|
collection_record_action: true,
|
|
62
62
|
bulk_action: true,
|
|
63
63
|
|
|
64
|
+
# Conditional visibility — display-only proc, NOT authorization (see below)
|
|
65
|
+
condition: -> { params[:beta] == "1" },
|
|
66
|
+
|
|
64
67
|
# Grouping
|
|
65
68
|
category: :primary, # :primary, :secondary, :danger
|
|
66
69
|
position: 50, # display order (lower = first)
|
|
@@ -84,6 +87,58 @@ def customize_actions
|
|
|
84
87
|
end
|
|
85
88
|
```
|
|
86
89
|
|
|
90
|
+
## Conditional visibility — `condition:`
|
|
91
|
+
|
|
92
|
+
Like the `condition:` proc on [inputs/displays/columns](/reference/resource/definition), an action can be **defined but only rendered when a runtime proc is truthy**. It's purely a toggle on whether the **button is shown** — the action (and its route) stays fully live either way.
|
|
93
|
+
|
|
94
|
+
The headline use case: **expose an action's endpoint without surfacing it in the UI** — e.g. one you call from the API, a webhook, or another service. Hide the button with an always-falsy condition; the route still works:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
# Defined and callable (API / programmatic), but no button anywhere in the UI:
|
|
98
|
+
action :sync_inventory, interaction: SyncInventoryInteraction, condition: -> { false }
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
It also works as a dynamic toggle driven by the **record** or the **view/request** context:
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
# object → the row/shown record (record & collection-record actions):
|
|
105
|
+
action :reopen, interaction: ReopenInteraction, condition: -> { object.closed? }
|
|
106
|
+
# view/request state — feature flag, preview/beta mode:
|
|
107
|
+
action :preview, interaction: PreviewInteraction, condition: -> { params[:beta] == "1" }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Inside the proc, `object`/`record` is the contextual record, and every other call delegates to the **view context**:
|
|
111
|
+
|
|
112
|
+
| Available | Notes |
|
|
113
|
+
|---|---|
|
|
114
|
+
| `object` / `record` | The row/shown record for **record** and **collection-record** actions; **`nil`** for resource and bulk actions (no single record). Guard with `object&.…` if a condition is shared across action kinds. |
|
|
115
|
+
| `params`, `request` | Current request. |
|
|
116
|
+
| `current_user`, `current_parent` | The signed-in user and (nested) parent. |
|
|
117
|
+
| `resource_record!` | The shown record on the show page; raises on index/table — prefer `object`. |
|
|
118
|
+
| `allowed_to?`, `policy_for`, other helpers | The usual view helpers. |
|
|
119
|
+
|
|
120
|
+
`object` is evaluated **per row** in tables and grids, so per-record show/hide works there too.
|
|
121
|
+
|
|
122
|
+
::: danger `condition:` is NOT authorization — it only hides the button
|
|
123
|
+
A hidden action still has a **live route**: anyone who knows the URL can still trigger it. `condition:` decides whether the *button renders*, never whether the *request is allowed*.
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
# 🚫 WRONG — this does NOT stop non-admins. The route is live; they can POST to it.
|
|
127
|
+
action :wipe, interaction: WipeInteraction, condition: -> { current_user.admin? }
|
|
128
|
+
|
|
129
|
+
# ✅ RIGHT — authorization belongs in the policy. The action only runs if this returns true.
|
|
130
|
+
class WidgetPolicy < ResourcePolicy
|
|
131
|
+
def wipe? = current_user.admin?
|
|
132
|
+
end
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Rule of thumb:** "who may run this" → **policy** (`def action_name?`). "is this UI relevant right now" → `condition:`. Authorization is enforced regardless of `condition:`; the two compose — an action appears only when the policy permits **and** the condition is truthy.
|
|
136
|
+
:::
|
|
137
|
+
|
|
138
|
+
::: tip Per-record display vs. per-record authorization
|
|
139
|
+
`condition: -> { object.draft? }` is fine for **showing/hiding** a per-record button. But if the rule is about **who may run it** ("only while draft *and* nobody else has it locked"), put it in the policy — `def publish? = record.draft?` is also evaluated per record (per row), and unlike `condition:` it actually gates execution.
|
|
140
|
+
:::
|
|
141
|
+
|
|
87
142
|
## Simple actions (navigation)
|
|
88
143
|
|
|
89
144
|
Link to an existing route. The target route MUST exist.
|
|
@@ -14,7 +14,7 @@ A **resource** is the unit Plutonium gives you full CRUD for — list, show, cre
|
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
16
|
rails g pu:res:scaffold Post user:belongs_to title:string 'content:text?' --dest=main_app
|
|
17
|
-
rails db:
|
|
17
|
+
rails db:prepare
|
|
18
18
|
rails g pu:res:conn Post --dest=admin_portal
|
|
19
19
|
```
|
|
20
20
|
|
|
@@ -42,7 +42,7 @@ module Pu
|
|
|
42
42
|
say_status :pending, "#{migration.version} - #{migration.name}", :yellow
|
|
43
43
|
end
|
|
44
44
|
say ""
|
|
45
|
-
say "Run `bin/rails db:
|
|
45
|
+
say "Run `bin/rails db:prepare` before generating TypeSpec specifications.", :red
|
|
46
46
|
raise Thor::Error, "Cannot generate TypeSpec with pending migrations"
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -26,7 +26,16 @@ module Pu
|
|
|
26
26
|
# Shell out to a fresh process so the sync reads the newly-installed
|
|
27
27
|
# gem's skills. Invoking in-process would reuse the already-loaded
|
|
28
28
|
# (pre-update) gem, whose Plutonium.root still points at the old skills.
|
|
29
|
-
|
|
29
|
+
#
|
|
30
|
+
# Must run unbundled: this process was booted with the pre-update gem,
|
|
31
|
+
# and its bundler environment (BUNDLE_GEMFILE, RUBYOPT=-rbundler/setup,
|
|
32
|
+
# the pinned load path) is inherited by the subprocess. Without clearing
|
|
33
|
+
# it, the "fresh" bin/rails re-uses the old locked gem set and syncs the
|
|
34
|
+
# stale skills anyway. with_unbundled_env lets it re-resolve from the
|
|
35
|
+
# updated Gemfile.lock and load the new gem.
|
|
36
|
+
Bundler.with_unbundled_env do
|
|
37
|
+
run "bin/rails generate pu:skills:sync"
|
|
38
|
+
end
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
def update_gem
|
|
@@ -135,7 +135,7 @@ module Pu
|
|
|
135
135
|
say "Next steps:"
|
|
136
136
|
say "\n"
|
|
137
137
|
say "1. Run migrations (if you haven't already):"
|
|
138
|
-
say " rails db:
|
|
138
|
+
say " rails db:prepare"
|
|
139
139
|
say "\n"
|
|
140
140
|
say "2. Customize the onboarding view to match your app:"
|
|
141
141
|
say " app/views/welcome/onboarding.html.erb"
|
|
@@ -6,7 +6,7 @@ module Plutonium
|
|
|
6
6
|
module Action
|
|
7
7
|
# Base class for all actions in the Plutonium framework.
|
|
8
8
|
class Base
|
|
9
|
-
attr_reader :name, :label, :description, :icon, :route_options, :confirmation, :turbo, :color, :category, :position, :return_to
|
|
9
|
+
attr_reader :name, :label, :description, :icon, :route_options, :confirmation, :turbo, :color, :category, :position, :return_to, :condition
|
|
10
10
|
|
|
11
11
|
def initialize(name, **options)
|
|
12
12
|
@name = name.to_sym
|
|
@@ -27,6 +27,7 @@ module Plutonium
|
|
|
27
27
|
@position = options[:position] || 50
|
|
28
28
|
@modal_mode = options[:modal]
|
|
29
29
|
@modal_size = options[:size]
|
|
30
|
+
@condition = options[:condition]
|
|
30
31
|
validate_modal_mode!
|
|
31
32
|
validate_modal_size!
|
|
32
33
|
|
|
@@ -62,6 +63,21 @@ module Plutonium
|
|
|
62
63
|
policy.allowed_to?(:"#{name}?")
|
|
63
64
|
end
|
|
64
65
|
|
|
66
|
+
# Display-only visibility gate, mirroring the `condition:` proc on
|
|
67
|
+
# inputs/displays/columns. Returns true when no condition is set.
|
|
68
|
+
#
|
|
69
|
+
# The proc is evaluated against a ConditionContext: `object`/`record` is
|
|
70
|
+
# the contextual record (nil for resource/bulk actions), and every other
|
|
71
|
+
# call delegates to the view context (current_user, params, request,
|
|
72
|
+
# allowed_to?, resource_record!, …).
|
|
73
|
+
#
|
|
74
|
+
# NOT an authorization boundary — a hidden action still has a live route;
|
|
75
|
+
# keep authorization in the policy.
|
|
76
|
+
def condition_met?(view_context, record: nil)
|
|
77
|
+
return true if @condition.nil?
|
|
78
|
+
ConditionContext.new(view_context, record).instance_exec(&@condition)
|
|
79
|
+
end
|
|
80
|
+
|
|
65
81
|
# Returns a new Action with the given options merged over this one.
|
|
66
82
|
def with(**overrides)
|
|
67
83
|
self.class.new(name, **to_options.merge(overrides))
|
|
@@ -90,7 +106,8 @@ module Plutonium
|
|
|
90
106
|
category: @category.to_sym,
|
|
91
107
|
position: @position,
|
|
92
108
|
modal: @modal_mode,
|
|
93
|
-
size: @modal_size
|
|
109
|
+
size: @modal_size,
|
|
110
|
+
condition: @condition
|
|
94
111
|
}
|
|
95
112
|
end
|
|
96
113
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "delegate"
|
|
4
|
+
|
|
5
|
+
module Plutonium
|
|
6
|
+
module Action
|
|
7
|
+
# Evaluation scope for an action's `condition:` proc.
|
|
8
|
+
#
|
|
9
|
+
# Exposes the contextual record as both `object` and `record`, and delegates
|
|
10
|
+
# everything else to the request's **view context** — the object render
|
|
11
|
+
# components forward their helpers to — so a condition can use current_user,
|
|
12
|
+
# params, request, allowed_to?, resource_record!, etc. directly, exactly
|
|
13
|
+
# like the `condition:` procs on inputs/displays/columns.
|
|
14
|
+
#
|
|
15
|
+
# Delegating to the view context (not a render component) matters: the
|
|
16
|
+
# component exposes params/request only as PRIVATE methods, which a delegator
|
|
17
|
+
# can't forward; the view context exposes them publicly.
|
|
18
|
+
#
|
|
19
|
+
# `record`/`object` is the row/shown record for record and
|
|
20
|
+
# collection-record actions, and nil for resource/bulk actions (no single
|
|
21
|
+
# record in scope) — so guard with `object&.…` in conditions shared across
|
|
22
|
+
# action kinds.
|
|
23
|
+
class ConditionContext < SimpleDelegator
|
|
24
|
+
attr_reader :record
|
|
25
|
+
alias_method :object, :record
|
|
26
|
+
|
|
27
|
+
def initialize(view_context, record)
|
|
28
|
+
super(view_context)
|
|
29
|
+
@record = record
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -232,12 +232,13 @@ module Plutonium
|
|
|
232
232
|
|
|
233
233
|
def row_actions
|
|
234
234
|
@row_actions ||= resource_definition.defined_actions.values.select { |a|
|
|
235
|
-
a.collection_record_action? && a.permitted_by?(record_policy)
|
|
235
|
+
a.collection_record_action? && a.permitted_by?(record_policy) && a.condition_met?(view_context, record:)
|
|
236
236
|
}
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
def can_show?
|
|
240
|
-
resource_definition.defined_actions[:show]
|
|
240
|
+
action = resource_definition.defined_actions[:show]
|
|
241
|
+
action&.permitted_by?(record_policy) && action.condition_met?(view_context, record:)
|
|
241
242
|
end
|
|
242
243
|
|
|
243
244
|
def record_policy
|
|
@@ -33,7 +33,7 @@ module Plutonium
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def page_actions
|
|
36
|
-
super || current_definition.defined_actions.values.select { |a| a.resource_action? && a.permitted_by?(current_policy) }
|
|
36
|
+
super || current_definition.defined_actions.values.select { |a| a.resource_action? && a.permitted_by?(current_policy) && a.condition_met?(view_context) }
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def render_default_content
|
|
@@ -15,7 +15,7 @@ module Plutonium
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def page_actions
|
|
18
|
-
super || current_definition.defined_actions.values.select { |a| a.record_action? && a.permitted_by?(current_policy) }
|
|
18
|
+
super || current_definition.defined_actions.values.select { |a| a.record_action? && a.permitted_by?(current_policy) && a.condition_met?(view_context, record: resource_record!) }
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def render_default_content
|
|
@@ -77,7 +77,7 @@ module Plutonium
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def link_classes(first = false, last = false)
|
|
80
|
-
base = "flex items-center justify-center w-9 h-9 text-[var(--pu-text-muted)] bg-[var(--pu-surface)] border border-[var(--pu-border)] hover:bg-[var(--pu-surface-alt)] hover:text-[var(--pu-text)] transition-colors"
|
|
80
|
+
base = "flex items-center justify-center min-w-9 h-9 px-2 text-[var(--pu-text-muted)] bg-[var(--pu-surface)] border border-[var(--pu-border)] hover:bg-[var(--pu-surface-alt)] hover:text-[var(--pu-text)] transition-colors"
|
|
81
81
|
classes = [base]
|
|
82
82
|
classes << "rounded-l-lg" if first
|
|
83
83
|
classes << "rounded-r-lg" if last
|
|
@@ -86,11 +86,11 @@ module Plutonium
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def current_link_classes
|
|
89
|
-
"flex items-center justify-center w-9 h-9 text-white bg-primary-600 border border-primary-600 rounded-lg font-medium cursor-default"
|
|
89
|
+
"flex items-center justify-center min-w-9 h-9 px-2 text-white bg-primary-600 border border-primary-600 rounded-lg font-medium cursor-default"
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def disabled_link_classes(first = false, last = false)
|
|
93
|
-
base = "flex items-center justify-center w-9 h-9 text-[var(--pu-text-subtle)] bg-[var(--pu-surface-alt)] border border-[var(--pu-border)] opacity-50 cursor-not-allowed"
|
|
93
|
+
base = "flex items-center justify-center min-w-9 h-9 px-2 text-[var(--pu-text-subtle)] bg-[var(--pu-surface-alt)] border border-[var(--pu-border)] opacity-50 cursor-not-allowed"
|
|
94
94
|
classes = [base]
|
|
95
95
|
classes << "rounded-l-lg" if first
|
|
96
96
|
classes << "rounded-r-lg" if last
|
|
@@ -134,7 +134,7 @@ module Plutonium
|
|
|
134
134
|
policy = policy_for(record:)
|
|
135
135
|
|
|
136
136
|
actions = resource_definition.defined_actions
|
|
137
|
-
.select { |k, a| a.collection_record_action? && policy.allowed_to?(:"#{k}?") }
|
|
137
|
+
.select { |k, a| a.collection_record_action? && policy.allowed_to?(:"#{k}?") && a.condition_met?(view_context, record:) }
|
|
138
138
|
.values
|
|
139
139
|
|
|
140
140
|
primary_actions = actions.select { |a| a.category.primary? }.sort_by(&:position)
|
|
@@ -161,7 +161,7 @@ module Plutonium
|
|
|
161
161
|
|
|
162
162
|
def bulk_actions
|
|
163
163
|
@bulk_actions ||= resource_definition.defined_actions
|
|
164
|
-
.select { |k, a| a.bulk_action? }
|
|
164
|
+
.select { |k, a| a.bulk_action? && a.condition_met?(view_context) }
|
|
165
165
|
.values
|
|
166
166
|
end
|
|
167
167
|
|
data/lib/plutonium/version.rb
CHANGED
data/package.json
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: plutonium
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.56.
|
|
4
|
+
version: 0.56.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stefan Froelich
|
|
@@ -956,6 +956,7 @@ files:
|
|
|
956
956
|
- lib/plutonium.rb
|
|
957
957
|
- lib/plutonium/action/README.md
|
|
958
958
|
- lib/plutonium/action/base.rb
|
|
959
|
+
- lib/plutonium/action/condition_context.rb
|
|
959
960
|
- lib/plutonium/action/interactive.rb
|
|
960
961
|
- lib/plutonium/action/route_options.rb
|
|
961
962
|
- lib/plutonium/action/simple.rb
|