plutonium 0.60.5 → 0.61.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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +19 -1
  3. data/.claude/skills/plutonium-app/SKILL.md +41 -0
  4. data/.claude/skills/plutonium-auth/SKILL.md +40 -0
  5. data/.claude/skills/plutonium-behavior/SKILL.md +47 -1
  6. data/.claude/skills/plutonium-kanban/SKILL.md +313 -0
  7. data/.claude/skills/plutonium-resource/SKILL.md +40 -0
  8. data/.claude/skills/plutonium-tenancy/SKILL.md +43 -0
  9. data/.claude/skills/plutonium-testing/SKILL.md +38 -0
  10. data/.claude/skills/plutonium-ui/SKILL.md +51 -0
  11. data/.claude/skills/plutonium-wizard/SKILL.md +469 -0
  12. data/.cliff.toml +6 -0
  13. data/Appraisals +3 -0
  14. data/CHANGELOG.md +549 -439
  15. data/CLAUDE.md +15 -7
  16. data/app/assets/plutonium.css +1 -1
  17. data/app/assets/plutonium.js +895 -193
  18. data/app/assets/plutonium.js.map +4 -4
  19. data/app/assets/plutonium.min.js +53 -53
  20. data/app/assets/plutonium.min.js.map +4 -4
  21. data/app/views/layouts/basic.html.erb +7 -0
  22. data/app/views/plutonium/_flash_toasts.html.erb +2 -46
  23. data/app/views/plutonium/_toast.html.erb +52 -0
  24. data/app/views/resource/_resource_kanban.html.erb +1 -0
  25. data/db/migrate/wizard/20260615000001_create_plutonium_wizard_sessions.rb +57 -0
  26. data/docs/.vitepress/config.ts +24 -0
  27. data/docs/guides/index.md +2 -0
  28. data/docs/guides/kanban.md +447 -0
  29. data/docs/guides/wizards.md +447 -0
  30. data/docs/public/images/guides/kanban-after-move.png +0 -0
  31. data/docs/public/images/guides/kanban-board-light.png +0 -0
  32. data/docs/public/images/guides/kanban-board.png +0 -0
  33. data/docs/public/images/guides/kanban-show-centered-modal.png +0 -0
  34. data/docs/public/images/guides/kanban-wip-toast.png +0 -0
  35. data/docs/public/images/guides/wizards-chooser.png +0 -0
  36. data/docs/public/images/guides/wizards-completed.png +0 -0
  37. data/docs/public/images/guides/wizards-index-action.png +0 -0
  38. data/docs/public/images/guides/wizards-repeater.png +0 -0
  39. data/docs/public/images/guides/wizards-review.png +0 -0
  40. data/docs/public/images/guides/wizards-step.png +0 -0
  41. data/docs/reference/behavior/policies.md +1 -1
  42. data/docs/reference/index.md +14 -0
  43. data/docs/reference/kanban/authorization.md +62 -0
  44. data/docs/reference/kanban/dsl.md +293 -0
  45. data/docs/reference/kanban/index.md +40 -0
  46. data/docs/reference/kanban/positioning.md +162 -0
  47. data/docs/reference/resource/definition.md +16 -0
  48. data/docs/reference/ui/forms.md +36 -0
  49. data/docs/reference/ui/pages.md +2 -0
  50. data/docs/reference/wizard/anchoring-resume.md +194 -0
  51. data/docs/reference/wizard/dsl.md +332 -0
  52. data/docs/reference/wizard/index.md +33 -0
  53. data/docs/reference/wizard/one-time.md +129 -0
  54. data/docs/reference/wizard/registration-launch.md +177 -0
  55. data/docs/reference/wizard/storage-config.md +151 -0
  56. data/docs/superpowers/plans/2026-06-14-form-sectioning.md +2 -2
  57. data/docs/superpowers/plans/2026-06-15-wizard-dsl.md +1619 -0
  58. data/docs/superpowers/plans/2026-06-15-wizard-dsl.md.tasks.json +68 -0
  59. data/docs/superpowers/plans/2026-06-26-kanban-dsl.md +1128 -0
  60. data/docs/superpowers/plans/2026-06-26-kanban-dsl.md.tasks.json +24 -0
  61. data/docs/superpowers/specs/2026-06-15-wizard-dsl-design.md +836 -0
  62. data/docs/superpowers/specs/2026-06-15-wizard-dsl-examples.rb +245 -0
  63. data/docs/superpowers/specs/2026-06-17-wizard-relaunch-prompt-design.md +86 -0
  64. data/docs/superpowers/specs/2026-06-18-wizard-attachments-design.md +101 -0
  65. data/docs/superpowers/specs/2026-06-18-wizard-hosting-design.md +220 -0
  66. data/docs/superpowers/specs/2026-06-26-kanban-dsl-design.md +388 -0
  67. data/gemfiles/postgres.gemfile +8 -0
  68. data/gemfiles/postgres.gemfile.lock +321 -0
  69. data/gemfiles/rails_7.gemfile +1 -0
  70. data/gemfiles/rails_7.gemfile.lock +1 -1
  71. data/gemfiles/rails_8.0.gemfile +1 -0
  72. data/gemfiles/rails_8.0.gemfile.lock +1 -1
  73. data/gemfiles/rails_8.1.gemfile +1 -0
  74. data/gemfiles/rails_8.1.gemfile.lock +14 -1
  75. data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +6 -1
  76. data/lib/plutonium/action/base.rb +9 -0
  77. data/lib/plutonium/auth/rodauth.rb +1 -2
  78. data/lib/plutonium/configuration.rb +4 -0
  79. data/lib/plutonium/core/controller.rb +20 -1
  80. data/lib/plutonium/definition/base.rb +25 -0
  81. data/lib/plutonium/definition/form_layout.rb +54 -35
  82. data/lib/plutonium/definition/index_views.rb +54 -1
  83. data/lib/plutonium/definition/wizards.rb +209 -0
  84. data/lib/plutonium/invites/concerns/invite_token.rb +9 -0
  85. data/lib/plutonium/invites/concerns/invite_user.rb +9 -0
  86. data/lib/plutonium/invites/controller.rb +4 -1
  87. data/lib/plutonium/kanban/action.rb +7 -0
  88. data/lib/plutonium/kanban/board.rb +40 -0
  89. data/lib/plutonium/kanban/broadcaster.rb +54 -0
  90. data/lib/plutonium/kanban/column.rb +69 -0
  91. data/lib/plutonium/kanban/context.rb +15 -0
  92. data/lib/plutonium/kanban/dsl.rb +71 -0
  93. data/lib/plutonium/kanban/grouping.rb +51 -0
  94. data/lib/plutonium/kanban/positioning.rb +75 -0
  95. data/lib/plutonium/kanban.rb +11 -0
  96. data/lib/plutonium/migrations.rb +40 -0
  97. data/lib/plutonium/positioning.rb +146 -0
  98. data/lib/plutonium/railtie.rb +33 -0
  99. data/lib/plutonium/resource/controller.rb +2 -0
  100. data/lib/plutonium/resource/controllers/crud_actions.rb +1 -1
  101. data/lib/plutonium/resource/controllers/kanban_actions.rb +455 -0
  102. data/lib/plutonium/resource/controllers/wizard_actions.rb +165 -0
  103. data/lib/plutonium/resource/policy.rb +8 -0
  104. data/lib/plutonium/routing/mapper_extensions.rb +44 -0
  105. data/lib/plutonium/routing/wizard_registration.rb +289 -0
  106. data/lib/plutonium/ui/display/resource.rb +17 -12
  107. data/lib/plutonium/ui/form/base.rb +19 -5
  108. data/lib/plutonium/ui/form/components/password.rb +126 -0
  109. data/lib/plutonium/ui/form/components/uppy.rb +6 -3
  110. data/lib/plutonium/ui/form/options/inferred_types.rb +20 -0
  111. data/lib/plutonium/ui/form/resource.rb +1 -1
  112. data/lib/plutonium/ui/form/wizard.rb +63 -0
  113. data/lib/plutonium/ui/grid/card.rb +16 -5
  114. data/lib/plutonium/ui/kanban/card.rb +67 -0
  115. data/lib/plutonium/ui/kanban/color_dot.rb +36 -0
  116. data/lib/plutonium/ui/kanban/column.rb +324 -0
  117. data/lib/plutonium/ui/kanban/resource.rb +212 -0
  118. data/lib/plutonium/ui/layout/resource_layout.rb +7 -1
  119. data/lib/plutonium/ui/modal/base.rb +30 -3
  120. data/lib/plutonium/ui/modal/centered.rb +5 -2
  121. data/lib/plutonium/ui/page/index.rb +1 -0
  122. data/lib/plutonium/ui/page/show.rb +23 -0
  123. data/lib/plutonium/ui/page/wizard.rb +371 -0
  124. data/lib/plutonium/ui/page/wizard_chooser.rb +97 -0
  125. data/lib/plutonium/ui/page/wizard_completed.rb +86 -0
  126. data/lib/plutonium/ui/table/base.rb +1 -1
  127. data/lib/plutonium/ui/table/components/view_switcher.rb +2 -1
  128. data/lib/plutonium/ui/wizard/review.rb +196 -0
  129. data/lib/plutonium/ui/wizard/stepper.rb +122 -0
  130. data/lib/plutonium/ui/wizard/summary_display.rb +59 -0
  131. data/lib/plutonium/version.rb +1 -1
  132. data/lib/plutonium/wizard/attachment_data.rb +42 -0
  133. data/lib/plutonium/wizard/attachments.rb +226 -0
  134. data/lib/plutonium/wizard/base.rb +216 -0
  135. data/lib/plutonium/wizard/base_controller.rb +31 -0
  136. data/lib/plutonium/wizard/configuration.rb +42 -0
  137. data/lib/plutonium/wizard/controller.rb +162 -0
  138. data/lib/plutonium/wizard/data.rb +134 -0
  139. data/lib/plutonium/wizard/driving.rb +639 -0
  140. data/lib/plutonium/wizard/dsl.rb +336 -0
  141. data/lib/plutonium/wizard/errors.rb +27 -0
  142. data/lib/plutonium/wizard/field_capture.rb +157 -0
  143. data/lib/plutonium/wizard/field_importer.rb +208 -0
  144. data/lib/plutonium/wizard/gate.rb +171 -0
  145. data/lib/plutonium/wizard/instance_key.rb +97 -0
  146. data/lib/plutonium/wizard/lazy_persisted.rb +77 -0
  147. data/lib/plutonium/wizard/resume.rb +250 -0
  148. data/lib/plutonium/wizard/review_step.rb +48 -0
  149. data/lib/plutonium/wizard/route_resolution.rb +40 -0
  150. data/lib/plutonium/wizard/runner.rb +684 -0
  151. data/lib/plutonium/wizard/session.rb +53 -0
  152. data/lib/plutonium/wizard/state.rb +35 -0
  153. data/lib/plutonium/wizard/step.rb +61 -0
  154. data/lib/plutonium/wizard/step_adapter.rb +103 -0
  155. data/lib/plutonium/wizard/store/active_record.rb +174 -0
  156. data/lib/plutonium/wizard/store/base.rb +42 -0
  157. data/lib/plutonium/wizard/store/memory.rb +44 -0
  158. data/lib/plutonium/wizard/sweep_job.rb +76 -0
  159. data/lib/plutonium/wizard.rb +86 -0
  160. data/lib/plutonium.rb +5 -0
  161. data/lib/rodauth/features/case_insensitive_login.rb +1 -1
  162. data/lib/tasks/release.rake +144 -191
  163. data/package.json +3 -3
  164. data/src/css/components.css +132 -0
  165. data/src/js/controllers/attachment_input_controller.js +15 -1
  166. data/src/js/controllers/dirty_form_guard_controller.js +155 -27
  167. data/src/js/controllers/kanban_controller.js +330 -0
  168. data/src/js/controllers/password_sentinel_controller.js +39 -0
  169. data/src/js/controllers/register_controllers.js +6 -0
  170. data/src/js/controllers/remote_modal_controller.js +10 -0
  171. data/src/js/controllers/row_click_controller.js +14 -1
  172. data/src/js/controllers/wizard_controller.js +54 -0
  173. data/src/js/turbo/turbo_confirm.js +1 -1
  174. data/yarn.lock +271 -282
  175. metadata +100 -1
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plutonium/positioning"
4
+
5
+ module Plutonium
6
+ module Kanban
7
+ module Positioning
8
+ # Value object passed to Mode B blocks, carrying the full drop context.
9
+ Move = Data.define(:record, :column, :prev, :next, :index)
10
+
11
+ # Strategy configuration object created by the `position_on` DSL.
12
+ #
13
+ # Three modes:
14
+ # Mode A (:delegate) — delegate reposition! to Plutonium::Positioning concern
15
+ # Mode B (:block) — call a user-supplied block with a Move
16
+ # Mode C (:disabled) — no ordering; relation returned unchanged
17
+ class Config
18
+ # Mode A, default attribute :position
19
+ def self.default
20
+ new(:delegate, :position, nil)
21
+ end
22
+
23
+ # Mode A, custom attribute
24
+ def self.attribute(attr)
25
+ new(:delegate, attr.to_sym, nil)
26
+ end
27
+
28
+ # Mode B — orders by attr, write delegated to block
29
+ def self.with_block(attr, block)
30
+ new(:block, attr.to_sym, block)
31
+ end
32
+
33
+ # Mode C — disabled
34
+ def self.disabled
35
+ new(:disabled, nil, nil)
36
+ end
37
+
38
+ attr_reader :attribute
39
+
40
+ def initialize(mode, attribute, block)
41
+ @mode = mode
42
+ @attribute = attribute
43
+ @block = block
44
+ end
45
+
46
+ def disabled?
47
+ @mode == :disabled
48
+ end
49
+
50
+ # Apply positional ordering to a relation.
51
+ # Mode A/B: relation.reorder(attribute)
52
+ # Mode C: return relation unchanged
53
+ def order(relation)
54
+ return relation if disabled?
55
+ relation.reorder(@attribute)
56
+ end
57
+
58
+ # Persist the new position for a dropped record.
59
+ # Mode A: delegate to record.reposition!(prev_record:, next_record:)
60
+ # Mode B: call the user block with a Move
61
+ # Mode C: no-op
62
+ def reposition!(record:, column:, prev_record:, next_record:, index:)
63
+ case @mode
64
+ when :delegate
65
+ record.reposition!(prev_record:, next_record:)
66
+ when :block
67
+ @block.call(Move.new(record:, column:, prev: prev_record, next: next_record, index:))
68
+ when :disabled
69
+ nil
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "plutonium/positioning"
4
+ require "plutonium/kanban/positioning"
5
+ require "plutonium/kanban/action"
6
+ require "plutonium/kanban/column"
7
+ require "plutonium/kanban/board"
8
+ require "plutonium/kanban/dsl"
9
+ require "plutonium/kanban/context"
10
+ require "plutonium/kanban/grouping"
11
+ require "plutonium/kanban/broadcaster"
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ # Registry of per-feature migration paths.
5
+ #
6
+ # Features register their migration directory against a configuration key. A
7
+ # path is only surfaced by {.enabled_paths} when its feature's config
8
+ # (+Plutonium.configuration.<feature>+) responds to +.enabled+ and it is true.
9
+ module Migrations
10
+ @registry = {}
11
+
12
+ class << self
13
+ # Register a migration path for a feature.
14
+ #
15
+ # @param feature [Symbol, String] the configuration key (e.g. +:wizards+)
16
+ # @param path [String] the absolute migration directory path
17
+ # @return [String] the registered path
18
+ def register(feature, path)
19
+ @registry[feature.to_sym] = path
20
+ end
21
+
22
+ # Clear the registry. Intended for tests.
23
+ #
24
+ # @return [Hash]
25
+ def reset!
26
+ @registry = {}
27
+ end
28
+
29
+ # Migration paths for features that are currently enabled.
30
+ #
31
+ # @return [Array<String>]
32
+ def enabled_paths
33
+ @registry.filter_map do |feature, path|
34
+ cfg = Plutonium.configuration.public_send(feature) if Plutonium.configuration.respond_to?(feature)
35
+ path if cfg&.respond_to?(:enabled) && cfg.enabled
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plutonium
4
+ # Standalone decimal/fractional ordering. Kanban-independent.
5
+ #
6
+ # Including this concern and calling `positioned_on` gives a model:
7
+ # - automatic position assignment on create (appends to the end of its scope group)
8
+ # - `reposition!(prev_record:, next_record:)` for drag-and-drop reordering
9
+ # - `backfill_positions!` class method to number existing rows
10
+ #
11
+ # Pure math helpers are exposed as module-level methods so they can be
12
+ # called without an AR instance:
13
+ # Plutonium::Positioning.position_between(1.0, 3.0) # => 2.0
14
+ # Plutonium::Positioning.gap_exhausted?(1.0, 1.0) # => true
15
+ module Positioning
16
+ extend ActiveSupport::Concern
17
+
18
+ EPSILON = 1e-6
19
+
20
+ # Returns the position that sits between +prev_val+ and +next_val+.
21
+ #
22
+ # Rules:
23
+ # both nil → 0.0 (first item in an empty list)
24
+ # prev nil → next_val - 1 (prepend)
25
+ # next nil → prev_val + 1 (append)
26
+ # else → midpoint
27
+ def self.position_between(prev_val, next_val)
28
+ return 0.0 if prev_val.nil? && next_val.nil?
29
+ return next_val - 1 if prev_val.nil?
30
+ return prev_val + 1 if next_val.nil?
31
+ (prev_val + next_val) / 2.0
32
+ end
33
+
34
+ # Returns true when +prev_val+ and +next_val+ are so close together
35
+ # that inserting a new midpoint would produce a duplicate.
36
+ def self.gap_exhausted?(prev_val, next_val)
37
+ return false if prev_val.nil? || next_val.nil?
38
+ (next_val - prev_val).abs < EPSILON
39
+ end
40
+
41
+ included do
42
+ class_attribute :positioning_column, instance_accessor: false, default: :position
43
+ class_attribute :positioning_scope_attr, instance_accessor: false, default: nil
44
+ end
45
+
46
+ class_methods do
47
+ # Opt in to positional ordering.
48
+ #
49
+ # positioned_on :position, scope: :status
50
+ #
51
+ # @param column [Symbol] the decimal column that stores positions
52
+ # @param scope [Symbol, nil] group rows by this column; nil = single global group
53
+ def positioned_on(column = :position, scope: nil)
54
+ self.positioning_column = column
55
+ self.positioning_scope_attr = scope
56
+ before_create :assign_initial_position
57
+ end
58
+
59
+ # Number every row in the table per scope group as 1.0, 2.0, … in
60
+ # +order+ order. Safe to call on an empty table.
61
+ #
62
+ # @param order [Symbol] column to sort by when assigning positions
63
+ def backfill_positions!(order: :created_at)
64
+ groups = positioning_scope_attr ? all.group_by(&positioning_scope_attr) : {nil => all.to_a}
65
+ groups.each_value do |rows|
66
+ ActiveRecord::Base.transaction do
67
+ rows.sort_by { |r| r.public_send(order) }.each_with_index do |row, i|
68
+ row.update_column(positioning_column, (i + 1).to_f)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ # Move this record so it sits between +prev_record+ and +next_record+
76
+ # within its scope group. Pass nil for either neighbor to move to an end.
77
+ #
78
+ # If the gap between the two neighbors is exhausted (too small to split)
79
+ # the scope group is rebalanced first so that fresh integer positions are
80
+ # available, then the record is positioned between the reloaded neighbors.
81
+ #
82
+ # @param prev_record [ActiveRecord::Base, nil]
83
+ # @param next_record [ActiveRecord::Base, nil]
84
+ def reposition!(prev_record:, next_record:)
85
+ col = self.class.positioning_column
86
+ prev_val = prev_record&.public_send(col)
87
+ next_val = next_record&.public_send(col)
88
+ if Plutonium::Positioning.gap_exhausted?(prev_val, next_val)
89
+ rebalance_scope_group!
90
+ prev_val = prev_record&.reload&.public_send(col)
91
+ next_val = next_record&.reload&.public_send(col)
92
+ end
93
+ update!(col => Plutonium::Positioning.position_between(prev_val, next_val))
94
+ end
95
+
96
+ private
97
+
98
+ def assign_initial_position
99
+ col = self.class.positioning_column
100
+ return if public_send(col).present?
101
+ max = positioning_group_relation.maximum(col) || 0.0
102
+ public_send(:"#{col}=", max + 1)
103
+ end
104
+
105
+ def positioning_group_relation
106
+ rel = self.class.all
107
+ attr = self.class.positioning_scope_attr
108
+ attr ? rel.where(attr => public_send(attr)) : rel
109
+ end
110
+
111
+ def rebalance_scope_group!
112
+ col = self.class.positioning_column
113
+ ActiveRecord::Base.transaction do
114
+ positioning_group_relation.order(col).each_with_index do |row, i|
115
+ row.update_column(col, (i + 1).to_f)
116
+ end
117
+ end
118
+ end
119
+
120
+ # Migration helper that adds a position column pre-tuned for fractional
121
+ # ordering. Mixed into ActiveRecord's table-definition classes by the
122
+ # railtie, so it is available in both create_table and change_table:
123
+ #
124
+ # create_table :tasks do |t|
125
+ # t.position # decimal :position, precision: 16, scale: 8
126
+ # t.position :sort_order # custom column name
127
+ # t.position index: true # also add a single-column index
128
+ # end
129
+ #
130
+ # change_table :tasks do |t|
131
+ # t.position
132
+ # end
133
+ #
134
+ # The precision/scale give midpoints ample headroom over the rebalance
135
+ # threshold (EPSILON = 1e-6) — a too-small scale lets the last subdivision
136
+ # round to a neighbor. Pass precision:/scale: to override.
137
+ module MigrationHelpers
138
+ DEFAULT_PRECISION = 16
139
+ DEFAULT_SCALE = 8
140
+
141
+ def position(name = :position, **options)
142
+ column name, :decimal, precision: DEFAULT_PRECISION, scale: DEFAULT_SCALE, **options
143
+ end
144
+ end
145
+ end
146
+ end
@@ -39,6 +39,30 @@ module Plutonium
39
39
  load_plutonium_initializers
40
40
  end
41
41
 
42
+ initializer "plutonium.register_migrations" do
43
+ Plutonium::Migrations.register(:wizards, Plutonium.root.join("db/migrate/wizard").to_s)
44
+ end
45
+
46
+ # Runs after the host's config/initializers/plutonium.rb (load_config_initializers)
47
+ # so feature flags like config.wizards.enabled are honoured. Railtie
48
+ # initializers otherwise run before host initializers, leaving the flag false.
49
+ initializer "plutonium.migrations", after: :load_config_initializers do |app|
50
+ # v1 supports `config.wizards.database == :primary` only; the option is
51
+ # reserved for future multi-db routing of wizard sessions. Fail loudly on an
52
+ # unsupported value rather than silently registering on the primary database.
53
+ if Plutonium.configuration.wizards.enabled &&
54
+ Plutonium.configuration.wizards.database != :primary
55
+ raise ArgumentError,
56
+ "config.wizards.database = #{Plutonium.configuration.wizards.database.inspect} is not " \
57
+ "supported (only :primary). Multi-database routing for wizard sessions is a roadmap follow-up."
58
+ end
59
+
60
+ Plutonium::Migrations.enabled_paths.each do |path|
61
+ app.config.paths["db/migrate"] << path
62
+ ActiveRecord::Migrator.migrations_paths << path unless ActiveRecord::Migrator.migrations_paths.include?(path)
63
+ end
64
+ end
65
+
42
66
  initializer "plutonium.asset_server" do
43
67
  setup_development_asset_server if Plutonium.configuration.development?
44
68
  end
@@ -47,6 +71,14 @@ module Plutonium
47
71
  extend_action_dispatch
48
72
  end
49
73
 
74
+ # Make `t.position` available in create_table / change_table blocks.
75
+ initializer "plutonium.positioning" do
76
+ ActiveSupport.on_load(:active_record) do
77
+ ActiveRecord::ConnectionAdapters::TableDefinition.include(Plutonium::Positioning::MigrationHelpers)
78
+ ActiveRecord::ConnectionAdapters::Table.include(Plutonium::Positioning::MigrationHelpers)
79
+ end
80
+ end
81
+
50
82
  initializer "plutonium.phlexi_themes" do
51
83
  setup_phlexi_themes
52
84
  end
@@ -97,6 +129,7 @@ module Plutonium
97
129
 
98
130
  def extend_action_dispatch
99
131
  ActionDispatch::Routing::Mapper.prepend Plutonium::Routing::MapperExtensions
132
+ ActionDispatch::Routing::Mapper.prepend Plutonium::Routing::WizardRegistration
100
133
  ActionDispatch::Routing::RouteSet.prepend Plutonium::Routing::RouteSetExtensions
101
134
  Rails::Engine.include Plutonium::Routing::ResourceRegistration
102
135
  end
@@ -14,7 +14,9 @@ module Plutonium
14
14
  include Plutonium::Resource::Controllers::Presentable
15
15
  include Plutonium::Resource::Controllers::Queryable
16
16
  include Plutonium::Resource::Controllers::CrudActions
17
+ include Plutonium::Resource::Controllers::KanbanActions
17
18
  include Plutonium::Resource::Controllers::InteractiveActions
19
+ include Plutonium::Resource::Controllers::WizardActions
18
20
  include Plutonium::Resource::Controllers::Typeahead
19
21
  include Plutonium::Resource::Controllers::ExportCsv
20
22
  include Plutonium::StructuredInputs::ParamsConcern
@@ -142,7 +142,7 @@ module Plutonium
142
142
  notice: "#{resource_class.model_name.human} was successfully deleted."
143
143
  end
144
144
  format.json { head :no_content }
145
- rescue ActiveRecord::InvalidForeignKey
145
+ rescue ActiveRecord::InvalidForeignKey, ActiveRecord::DeleteRestrictionError
146
146
  format.turbo_stream do
147
147
  flash.alert = "#{resource_class.model_name.human} is referenced by other records."
148
148
  render turbo_stream: helpers.turbo_stream_redirect(resource_url_for(resource_record!))