alembic 0.1.0 → 0.2.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -22
  3. data/app/assets/builds/alembic/page_builder.css +1 -1
  4. data/app/assets/builds/alembic/page_builder.js +3 -3
  5. data/app/controllers/alembic/application_controller.rb +0 -16
  6. data/app/controllers/alembic/flows_controller.rb +6 -90
  7. data/app/controllers/alembic/manage/base_controller.rb +1 -0
  8. data/app/controllers/alembic/manage/flows_controller.rb +7 -44
  9. data/app/controllers/alembic/manage/pages_controller.rb +8 -1
  10. data/app/controllers/alembic/manage/previews_controller.rb +4 -19
  11. data/app/controllers/alembic/pages_controller.rb +19 -0
  12. data/app/controllers/concerns/alembic/summarizes.rb +38 -0
  13. data/app/helpers/alembic/pages_helper.rb +39 -0
  14. data/app/models/alembic/flow/definition_summary.rb +9 -0
  15. data/app/models/alembic/flow/run_summary.rb +10 -0
  16. data/app/models/alembic/flow/summaries.rb +36 -5
  17. data/app/models/alembic/flow/summary_version.rb +1 -1
  18. data/app/models/alembic/outputs/grouped.rb +1 -1
  19. data/app/models/alembic/outputs/percentage.rb +1 -1
  20. data/app/models/alembic/outputs/weighted_sum.rb +1 -1
  21. data/app/models/alembic/page/drawing.rb +17 -0
  22. data/app/models/alembic/page/options.rb +46 -0
  23. data/app/models/alembic/page/version.rb +13 -0
  24. data/app/models/alembic/page.rb +32 -0
  25. data/app/views/alembic/flows/complete.html.erb +3 -3
  26. data/app/views/alembic/flows/show.html.erb +6 -6
  27. data/app/views/alembic/manage/flows/edit.html.erb +10 -10
  28. data/app/views/alembic/manage/pages/_block_content.html.erb +1 -1
  29. data/app/views/alembic/manage/pages/index.html.erb +4 -1
  30. data/app/views/alembic/manage/pages/show.html.erb +7 -1
  31. data/app/views/alembic/pages/show.html.erb +5 -0
  32. data/config/routes.rb +8 -19
  33. data/db/migrate/20260825224538_remove_published_version_from_alembic_diagnostics.rb +21 -1
  34. data/db/migrate/20260922120000_add_slug_to_alembic_pages.rb +6 -0
  35. data/db/migrate/20260922130000_create_alembic_page_versions.rb +16 -0
  36. data/db/migrate/20260924220000_move_flows_onto_easy_flow.rb +166 -0
  37. data/lib/alembic/engine.rb +2 -11
  38. data/lib/alembic/version.rb +1 -1
  39. data/lib/alembic.rb +30 -6
  40. metadata +35 -71
  41. data/app/assets/builds/alembic/canvas.js +0 -1
  42. data/app/controllers/alembic/manage/canvas_controller.rb +0 -183
  43. data/app/controllers/alembic/manage/definitions_controller.rb +0 -15
  44. data/app/controllers/alembic/manage/versions_controller.rb +0 -22
  45. data/app/controllers/concerns/alembic/manage/draws_canvas.rb +0 -35
  46. data/app/javascript/alembic/canvas/Canvas.jsx +0 -197
  47. data/app/javascript/alembic/canvas/Connector.jsx +0 -30
  48. data/app/javascript/alembic/canvas/ConnectorLayer.jsx +0 -31
  49. data/app/javascript/alembic/canvas/Control.jsx +0 -42
  50. data/app/javascript/alembic/canvas/Inspector.jsx +0 -44
  51. data/app/javascript/alembic/canvas/Panel.jsx +0 -67
  52. data/app/javascript/alembic/canvas/Placeholder.jsx +0 -24
  53. data/app/javascript/alembic/canvas/Port.jsx +0 -16
  54. data/app/javascript/alembic/canvas/Records.jsx +0 -34
  55. data/app/javascript/alembic/canvas/StepCard.jsx +0 -57
  56. data/app/javascript/alembic/canvas/Toolbar.jsx +0 -19
  57. data/app/javascript/alembic/canvas/TypePicker.jsx +0 -21
  58. data/app/javascript/alembic/canvas/changes.js +0 -5
  59. data/app/javascript/alembic/canvas/choices.js +0 -4
  60. data/app/javascript/alembic/canvas/flow.js +0 -7
  61. data/app/javascript/alembic/canvas/flowSender.js +0 -22
  62. data/app/javascript/alembic/canvas/ids.js +0 -7
  63. data/app/javascript/alembic/canvas/rows.js +0 -2
  64. data/app/javascript/alembic/canvas/styles.js +0 -3
  65. data/app/javascript/alembic/canvas/useConnectors.js +0 -75
  66. data/app/javascript/alembic/canvas/useFlow.js +0 -17
  67. data/app/javascript/alembic/canvas.jsx +0 -13
  68. data/app/models/alembic/asked.rb +0 -3
  69. data/app/models/alembic/choice.rb +0 -7
  70. data/app/models/alembic/flow/admission.rb +0 -19
  71. data/app/models/alembic/flow/canvas.rb +0 -167
  72. data/app/models/alembic/flow/change.rb +0 -18
  73. data/app/models/alembic/flow/condition.rb +0 -24
  74. data/app/models/alembic/flow/definition.rb +0 -132
  75. data/app/models/alembic/flow/digest.rb +0 -116
  76. data/app/models/alembic/flow/document.rb +0 -131
  77. data/app/models/alembic/flow/drawing.rb +0 -15
  78. data/app/models/alembic/flow/edge.rb +0 -9
  79. data/app/models/alembic/flow/edit_history.rb +0 -75
  80. data/app/models/alembic/flow/layout.rb +0 -88
  81. data/app/models/alembic/flow/name.rb +0 -15
  82. data/app/models/alembic/flow/node.rb +0 -5
  83. data/app/models/alembic/flow/output.rb +0 -23
  84. data/app/models/alembic/flow/progress/kept.rb +0 -38
  85. data/app/models/alembic/flow/progress/loose.rb +0 -43
  86. data/app/models/alembic/flow/progress.rb +0 -11
  87. data/app/models/alembic/flow/registry.rb +0 -25
  88. data/app/models/alembic/flow/run.rb +0 -54
  89. data/app/models/alembic/flow/runner.rb +0 -65
  90. data/app/models/alembic/flow/settings.rb +0 -80
  91. data/app/models/alembic/flow/start.rb +0 -13
  92. data/app/models/alembic/flow/step.rb +0 -42
  93. data/app/models/alembic/flow/step_type/declaration.rb +0 -118
  94. data/app/models/alembic/flow/step_type.rb +0 -69
  95. data/app/models/alembic/flow/switch.rb +0 -17
  96. data/app/models/alembic/flow/terminal.rb +0 -13
  97. data/app/models/alembic/flow/validator.rb +0 -171
  98. data/app/models/alembic/flow/version.rb +0 -26
  99. data/app/models/alembic/flow/violation.rb +0 -9
  100. data/app/models/alembic/flow.rb +0 -44
  101. data/app/models/alembic/runner.rb +0 -13
  102. data/app/models/alembic/steps/question.rb +0 -52
  103. data/app/views/alembic/flow/steps/_choosing.html.erb +0 -9
  104. data/app/views/alembic/flows/step.html.erb +0 -27
  105. data/app/views/alembic/manage/definitions/edit.html.erb +0 -13
  106. data/app/views/alembic/manage/flows/index.html.erb +0 -32
  107. data/app/views/alembic/manage/flows/show.html.erb +0 -26
  108. data/app/views/alembic/manage/versions/index.html.erb +0 -41
@@ -1,34 +1,14 @@
1
1
  module Alembic
2
- class FlowsController < ApplicationController
3
- helper_method :flow_start_path, :flow_step_path, :previewing?, :step_form, :carries_answers?
2
+ class FlowsController < EasyFlow::FlowsController
3
+ hosted_by FLOW_HOST
4
+ include Summarizes
4
5
 
5
- def show
6
- @diagnostic = flow
7
- end
8
-
9
- def start
10
- redirect_to alembic.run_path(Flow::Run.start(flow))
11
- end
12
-
13
- def step
14
- @guide = Runner.new(running_definition)
15
- @progress = progress
16
- @guide.run(@progress)
17
- @answers = @progress.recorded
18
- @question = @guide.next_step(@answers)
19
- @drawing = @guide.drawing_at(@answers)
20
- return render :step if @question
21
-
22
- render_completion
23
- end
6
+ private
24
7
 
25
- def update
26
- params[:back] ? progress.discard_last : record_submitted
27
- redirect_to alembic.run_path(run)
8
+ def run_location(run)
9
+ alembic.run_path(run)
28
10
  end
29
11
 
30
- private
31
-
32
12
  def flow_start_path(slug)
33
13
  alembic.flow_path(slug)
34
14
  end
@@ -36,69 +16,5 @@ module Alembic
36
16
  def flow_step_path(slug)
37
17
  alembic.flow_step_path(slug)
38
18
  end
39
-
40
- def previewing?
41
- false
42
- end
43
-
44
- def step_form
45
- return { url: alembic.run_path(run), method: :patch } if run
46
-
47
- { url: flow_step_path(@guide.slug), method: :get }
48
- end
49
-
50
- def carries_answers?
51
- run.nil?
52
- end
53
-
54
- def render_completion
55
- @answered = @guide.state_on_path(@answers)
56
- @progress.finish(@answered)
57
- @outputs = @progress.summary_of(@answered.transform_keys(&:to_s))
58
- render :complete
59
- end
60
-
61
- def progress
62
- return @progress ||= Flow::Progress.for(run.flow, run: run) if run
63
-
64
- @progress ||= Flow::Progress.for(flow, answers: submitted_answers, definition: running_definition)
65
- end
66
-
67
- def running_definition
68
- @running_definition ||= run ? run.pinned_definition : flowing_definition(flow)
69
- end
70
-
71
- def run
72
- return unless params[:id]
73
-
74
- @run ||= admitted_run
75
- end
76
-
77
- def admitted_run
78
- found = Flow::Run.find(params[:id])
79
-
80
- Flow::Admission.of_run(found, permitted: permitted?(found.flow))
81
- end
82
-
83
- def flow
84
- @stored_flow ||= admit(Flow::Definition.find_by(slug: params[:slug]))
85
- end
86
-
87
- def flowing_definition(diagnostic)
88
- diagnostic.live_definition
89
- end
90
-
91
- def submitted_answers
92
- params.fetch(:answers, {}).permit(*@guide.steps.map(&:id)).to_h.symbolize_keys
93
- end
94
-
95
- def record_submitted
96
- id, value = params.fetch(:answers, {}).permit(*asked).to_h.first
97
- progress.record(id, value)
98
- end
99
-
100
- def asked
101
- Runner.new(run.pinned_definition).steps.map(&:id)
102
- end
103
19
  end
104
20
  end
@@ -11,6 +11,7 @@ module Alembic
11
11
  helper KeystoneUiHelper
12
12
  helper KeystoneUi::React::MountHelper
13
13
  helper KsBlocks::ContentHelper
14
+ helper Alembic::PagesHelper
14
15
  end
15
16
  end
16
17
  end
@@ -1,54 +1,17 @@
1
1
  module Alembic
2
2
  module Manage
3
- class FlowsController < BaseController
4
- include DrawsCanvas
5
- def index
6
- @diagnostics = ordered_diagnostics
7
- end
8
-
9
- def create
10
- @diagnostic = Flow::Definition.new(create_params)
11
-
12
- if @diagnostic.save
13
- redirect_to manage_flow_path(@diagnostic), notice: "Flow created."
14
- else
15
- @diagnostics = ordered_diagnostics
16
- render :index, status: :unprocessable_entity
17
- end
18
- end
19
-
20
- def show
21
- @diagnostic = Flow::Definition.find(params[:id])
22
- @canvas = canvas_payload(@diagnostic)
23
- end
24
-
3
+ class FlowsController < EasyFlow::Manage::FlowsController
4
+ hosted_by FLOW_HOST
25
5
  def edit
26
- @diagnostic = Flow::Definition.find(params[:id])
6
+ super
7
+ @summary = Flow::Summaries.new(@flow).text
27
8
  end
28
9
 
29
10
  def update
30
- @diagnostic = Flow::Definition.find(params[:id])
31
- @diagnostic.update!(diagnostic_params)
32
- redirect_to manage_flow_path(@diagnostic), notice: "Saved."
33
- end
34
-
35
- def destroy
36
- Flow::Definition.find(params[:id]).destroy!
37
- redirect_to manage_flows_path, notice: "Flow removed."
38
- end
39
-
40
- private
41
-
42
- def ordered_diagnostics
43
- Flow::Definition.order(:slug)
44
- end
45
-
46
- def create_params
47
- params.require(:flow).permit(:slug, :kind)
48
- end
11
+ summary = params.require(:flow)[:summary]
12
+ Flow::Summaries.new(flow_host.flows.find(params[:id])).describe(summary) unless summary.nil?
49
13
 
50
- def diagnostic_params
51
- params.require(:flow).permit(:title, :summary, :start_label, :persists)
14
+ super
52
15
  end
53
16
  end
54
17
  end
@@ -20,6 +20,13 @@ module Alembic
20
20
  redirect_to manage_page_path(page)
21
21
  end
22
22
 
23
+ def publish
24
+ page = Page.find(params[:id])
25
+ page.publish
26
+
27
+ redirect_to manage_page_path(page)
28
+ end
29
+
23
30
  private
24
31
 
25
32
  def payload(page)
@@ -31,7 +38,7 @@ module Alembic
31
38
  end
32
39
 
33
40
  def page_params
34
- params.require(:page).permit(:name)
41
+ params.require(:page).permit(:name, :slug)
35
42
  end
36
43
  end
37
44
  end
@@ -1,10 +1,11 @@
1
1
  module Alembic
2
2
  module Manage
3
- class PreviewsController < ::Alembic::FlowsController
4
- include AuthenticatesAdmin
3
+ class PreviewsController < EasyFlow::Manage::PreviewsController
4
+ hosted_by FLOW_HOST
5
+ include Summarizes
5
6
 
6
7
  def show
7
- @diagnostic = previewed
8
+ @flow = previewed
8
9
  render template: "alembic/flows/show"
9
10
  end
10
11
 
@@ -17,22 +18,6 @@ module Alembic
17
18
  def flow_step_path(_slug)
18
19
  alembic.step_manage_flow_preview_path(previewed)
19
20
  end
20
-
21
- def previewing?
22
- true
23
- end
24
-
25
- def flowing_definition(diagnostic)
26
- diagnostic.document
27
- end
28
-
29
- def previewed
30
- @previewed ||= Flow::Definition.find(params[:flow_id])
31
- end
32
-
33
- def admit(_diagnostic)
34
- previewed
35
- end
36
21
  end
37
22
  end
38
23
  end
@@ -0,0 +1,19 @@
1
+ module Alembic
2
+ class PagesController < ApplicationController
3
+ helper PagesHelper
4
+ helper KsBlocks::LayoutHelper
5
+
6
+ def show
7
+ @page = shown_page
8
+ end
9
+
10
+ private
11
+
12
+ def shown_page
13
+ page = Page.find_by(slug: params[:slug])
14
+ raise NotPublished if page.nil? || page.live_version.nil?
15
+
16
+ page
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,38 @@
1
+ module Alembic
2
+ module Summarizes
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ layout -> { Alembic.layout }
7
+ helper ApplicationHelper
8
+ helper_method :flow_summary, :first_step_path
9
+ end
10
+
11
+ private
12
+
13
+ def flow_summary(flow)
14
+ Flow::Summaries.new(flow).text
15
+ end
16
+
17
+ def first_step_path(slug)
18
+ flow_step_path(slug)
19
+ end
20
+
21
+ def start_run(flow)
22
+ super.tap { |run| Flow::Summaries.new(flow).pin(run) }
23
+ end
24
+
25
+ def finished(answers, finished_run)
26
+ @outputs = outputs_of(answers.transform_keys(&:to_s))
27
+ Flow::Summaries.new(flow).pin(finished_run) if finished_run && run.nil?
28
+ render template: "alembic/flows/complete"
29
+ end
30
+
31
+ def outputs_of(state)
32
+ return Flow::Summaries.new(run.flow).of_run(run, state) if run
33
+
34
+ summaries = Flow::Summaries.new(flow)
35
+ summaries.any? ? summaries.of(state) : []
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,39 @@
1
+ require "ks_blocks/layout_helper"
2
+
3
+ module Alembic
4
+ module PagesHelper
5
+ UNDRAWABLE = "This block cannot be drawn until its fields are filled in.".freeze
6
+
7
+ def drawn_page(page, being_edited: false)
8
+ blocks = being_edited ? page.blocks : page.live_version&.blocks.to_a
9
+ drawn = blocks.to_h { |block| [ block["id"], drawn_block(block) ] }.compact
10
+
11
+ block_layout(blocks.select { |block| drawn.key?(block["id"]) }, kind: :pages) do |block|
12
+ drawn.fetch(block["id"])
13
+ end
14
+ end
15
+
16
+ def drawn_block(block)
17
+ component = Page::Drawing.of(block["type"])
18
+ return unless component
19
+
20
+ body = Page::Options.body_for(block)
21
+ body.nil? ? send(component, **Page::Options.for(block)) : send(component, **Page::Options.for(block)) { body }
22
+ rescue StandardError => undrawable
23
+ Rails.logger.error("Alembic could not draw the #{block['type']} block: #{undrawable.message}")
24
+ nil
25
+ end
26
+
27
+ def shown_block(block)
28
+ drawn_block(block) || undrawn_block(block)
29
+ end
30
+
31
+ private
32
+
33
+ def undrawn_block(block)
34
+ return UNDRAWABLE if Page::Drawing.of(block["type"])
35
+
36
+ KsBlocks.registry.block_types(kind: :pages).find { |type| type.key.to_s == block["type"] }&.name
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ module Alembic
2
+ module Flow
3
+ class DefinitionSummary < ApplicationRecord
4
+ self.table_name = "alembic_flow_definition_summaries"
5
+
6
+ belongs_to :flow, class_name: "EasyFlow::Definition"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Alembic
2
+ module Flow
3
+ class RunSummary < ApplicationRecord
4
+ self.table_name = "alembic_flow_run_summaries"
5
+
6
+ belongs_to :run, class_name: "EasyFlow::Run"
7
+ belongs_to :summary_version
8
+ end
9
+ end
10
+ end
@@ -10,12 +10,12 @@ module Alembic
10
10
  end
11
11
 
12
12
  def current_version
13
- @flow.summary_versions.find_by(number: cursor)
13
+ versions.find_by(number: cursor)
14
14
  end
15
15
 
16
16
  def record(payload)
17
- @flow.summary_versions.create!(number: next_number, summary: payload)
18
- .tap { |version| @flow.update!(summary_cursor: version.number) }
17
+ versions.create!(number: next_number, summary: payload)
18
+ .tap { |version| details.update!(summary_cursor: version.number) }
19
19
  end
20
20
 
21
21
  def any?
@@ -26,18 +26,49 @@ module Alembic
26
26
  Summary::Report.new(document).results(Summary::Run.new(state: state, steps: steps_by_id))
27
27
  end
28
28
 
29
+ def text
30
+ details.summary
31
+ end
32
+
33
+ def describe(text)
34
+ details.update!(summary: text)
35
+ end
36
+
37
+ def pin(run)
38
+ RunSummary.create!(run: run, summary_version: current_version) if current_version
39
+ end
40
+
41
+ def pinned_to(run)
42
+ RunSummary.find_by(run: run)&.summary_version
43
+ end
44
+
45
+ def of_run(run, state)
46
+ pinned = pinned_to(run)&.summary
47
+ return [] if pinned.blank?
48
+
49
+ Summary::Report.new(pinned).results(Summary::Run.new(state: state, steps: run.pinned_steps))
50
+ end
51
+
29
52
  private
30
53
 
54
+ def versions
55
+ SummaryVersion.where(flow: @flow)
56
+ end
57
+
58
+ def details
59
+ @details ||= DefinitionSummary.find_or_initialize_by(flow: @flow)
60
+ end
61
+
31
62
  def steps_by_id
32
63
  Array(@flow.definition.to_h["nodes"]).index_by { |node| node["id"] }
33
64
  end
34
65
 
35
66
  def cursor
36
- @flow.summary_cursor || @flow.summary_versions.pluck(:number).max
67
+ details.summary_cursor || versions.maximum(:number)
37
68
  end
38
69
 
39
70
  def next_number
40
- (@flow.summary_versions.maximum(:number) || 0) + 1
71
+ (versions.maximum(:number) || 0) + 1
41
72
  end
42
73
  end
43
74
  end
@@ -3,7 +3,7 @@ module Alembic
3
3
  class SummaryVersion < ApplicationRecord
4
4
  self.table_name = "alembic_flow_summaries"
5
5
 
6
- belongs_to :flow, class_name: "Alembic::Flow::Definition"
6
+ belongs_to :flow, class_name: "EasyFlow::Definition"
7
7
 
8
8
  validates :number, uniqueness: { scope: :flow_id }
9
9
 
@@ -13,7 +13,7 @@ module Alembic
13
13
  end
14
14
 
15
15
  def self.bucket(step, marker)
16
- marker ? step[marker] : Steps::Question.category_of(step)
16
+ marker ? step[marker] : EasyFlow::Steps::Question.category_of(step)
17
17
  end
18
18
 
19
19
  def self.shares(run, marker)
@@ -24,7 +24,7 @@ module Alembic
24
24
  end
25
25
 
26
26
  def self.most(step)
27
- Steps::Question.answers_of(step).filter_map { |option| option["weight"].to_i if option.is_a?(Hash) }.max.to_i
27
+ EasyFlow::Steps::Question.answers_of(step).filter_map { |option| option["weight"].to_i if option.is_a?(Hash) }.max.to_i
28
28
  end
29
29
  end
30
30
  end
@@ -17,7 +17,7 @@ module Alembic
17
17
  end
18
18
 
19
19
  def self.weight_of(step, value)
20
- chosen = Steps::Question.answers_of(step).find { |option| option.is_a?(Hash) && option["value"] == value }
20
+ chosen = EasyFlow::Steps::Question.answers_of(step).find { |option| option.is_a?(Hash) && option["value"] == value }
21
21
 
22
22
  chosen&.fetch("weight", nil).to_i
23
23
  end
@@ -0,0 +1,17 @@
1
+ module Alembic
2
+ class Page < ApplicationRecord
3
+ module Drawing
4
+ def self.record(key, component)
5
+ components[key.to_sym] = component
6
+ end
7
+
8
+ def self.of(key)
9
+ components[key.to_sym]
10
+ end
11
+
12
+ def self.components
13
+ @components ||= {}
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,46 @@
1
+ module Alembic
2
+ class Page < ApplicationRecord
3
+ module Options
4
+ def self.declare(key, options, body: nil)
5
+ declared[key.to_sym] = options
6
+ bodies[key.to_sym] = body
7
+ end
8
+
9
+ def self.for(block)
10
+ named(filled(block).except(bodies[block["type"].to_sym]), declared.fetch(block["type"].to_sym, {}))
11
+ end
12
+
13
+ def self.body_for(block)
14
+ field = bodies[block["type"].to_sym]
15
+
16
+ filled(block)[field] if field
17
+ end
18
+
19
+ def self.bodies
20
+ @bodies ||= {}
21
+ end
22
+
23
+ def self.declared
24
+ @declared ||= {}
25
+ end
26
+
27
+ def self.filled(block)
28
+ block.fetch("content", {}).reject { |_key, value| value == "" }.symbolize_keys
29
+ end
30
+
31
+ def self.named(filled, options)
32
+ options.each_with_object(filled) do |(option, spec), built|
33
+ spec.is_a?(Hash) ? fill(built, option, spec) : built[option] = spec
34
+ end
35
+ end
36
+
37
+ def self.fill(built, option, spec)
38
+ value = built.delete(spec.fetch(:from, option))
39
+ built[option] = value.nil? ? spec[:default] : value
40
+ built.delete(option) if built[option].nil?
41
+ end
42
+
43
+ private_class_method :filled, :named, :fill
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,13 @@
1
+ module Alembic
2
+ class Page < ApplicationRecord
3
+ class Version < ApplicationRecord
4
+ self.table_name = "alembic_page_versions"
5
+
6
+ enum :status, { draft: "draft", live: "live", superseded: "superseded" }
7
+
8
+ belongs_to :page, class_name: "Alembic::Page"
9
+
10
+ validates :number, uniqueness: { scope: :page_id }
11
+ end
12
+ end
13
+ end
@@ -6,6 +6,38 @@ module Alembic
6
6
 
7
7
  block_layout :blocks, kind: :pages
8
8
 
9
+ has_many :versions, class_name: "Alembic::Page::Version", dependent: :destroy, inverse_of: :page
10
+
9
11
  validates :name, presence: true
12
+ validates :slug, uniqueness: true, allow_nil: true
13
+
14
+ def publish
15
+ transaction do
16
+ live_version&.update!(status: :superseded)
17
+ versions.create!(number: next_number, blocks: blocks, status: :live)
18
+ end
19
+ end
20
+
21
+ def live_version
22
+ versions.find_by(status: :live)
23
+ end
24
+
25
+ def next_number
26
+ (versions.maximum(:number) || 0) + 1
27
+ end
28
+
29
+ def self.block(key, drawn_by: nil, options: {}, body: nil, **block_type)
30
+ refuse_unknown(key, drawn_by)
31
+ Drawing.record(key, drawn_by)
32
+ Options.declare(key, options, body: body)
33
+ KsBlocks.block(key, kind: :pages, **block_type)
34
+ end
35
+
36
+ def self.refuse_unknown(key, component)
37
+ return if component.nil? || ApplicationController.helpers.respond_to?(component)
38
+
39
+ raise ArgumentError, "The #{key} page block names #{component}, which nothing draws"
40
+ end
41
+ private_class_method :refuse_unknown
10
42
  end
11
43
  end
@@ -22,11 +22,11 @@
22
22
  <% end %>
23
23
 
24
24
  <%= ui_section(title: @outputs.any? ? "What you told us" : nil) do %>
25
- <ul class="divide-y divide-gray-100 border-y border-gray-100">
25
+ <ul class="divide-y divide-(color:--ks-color-divider) border-y border-(color:--ks-color-divider) dark:divide-(color:--ks-color-divider-dark) dark:border-(color:--ks-color-divider-dark)">
26
26
  <% @answered.each do |step, value| %>
27
27
  <li data-answer="<%= step %>" class="flex items-baseline justify-between gap-6 py-3">
28
- <span class="text-gray-600"><%= @guide.question_text(step) %></span>
29
- <span class="shrink-0 font-medium text-gray-900"><%= @guide.choice_label(step, value) %></span>
28
+ <span class="ks-tone-muted"><%= @guide.question_text(step) %></span>
29
+ <span class="shrink-0 font-(weight:--ks-font-weight-medium) ks-tone-neutral"><%= @guide.choice_label(step, value) %></span>
30
30
  </li>
31
31
  <% end %>
32
32
  </ul>
@@ -1,16 +1,16 @@
1
- <% content_for(:title, @diagnostic.title.presence || @diagnostic.slug) %>
1
+ <% content_for(:title, @flow.title.presence || @flow.slug) %>
2
2
 
3
3
  <%= ui_page(max_width: :md) do %>
4
- <%= ui_page_header(title: @diagnostic.title.presence || @diagnostic.slug, subtitle: @diagnostic.summary) %>
4
+ <%= ui_page_header(title: @flow.title.presence || @flow.slug, subtitle: flow_summary(@flow)) %>
5
5
 
6
6
  <%= ui_section do %>
7
7
  <div class="flex flex-wrap items-center gap-3">
8
- <% if @diagnostic.each_step? && @diagnostic.definition.present? && !previewing? %>
9
- <%= ui_form(action: alembic.flow_runs_path(@diagnostic.slug), method: :post) do %>
10
- <%= ui_button(label: @diagnostic.start_label.presence || "Start", type: :submit) %>
8
+ <% if @flow.each_step? && @flow.definition.present? && !previewing? %>
9
+ <%= ui_form(action: alembic.flow_runs_path(@flow.slug), method: :post) do %>
10
+ <%= ui_button(label: @flow.start_label.presence || "Start", type: :submit) %>
11
11
  <% end %>
12
12
  <% else %>
13
- <%= ui_button(label: @diagnostic.start_label.presence || "Start", href: flow_step_path(@diagnostic.slug)) %>
13
+ <%= ui_button(label: @flow.start_label.presence || "Start", href: first_step_path(@flow.slug)) %>
14
14
  <% end %>
15
15
  </div>
16
16
  <% end %>
@@ -1,24 +1,24 @@
1
1
  <%= ui_page(max_width: :lg) do %>
2
2
  <p class="mb-4">
3
- <%= link_to "← Back to the flow", alembic.manage_flow_path(@diagnostic), class: "text-sm text-accent-600 hover:text-accent-700" %>
3
+ <%= link_to "← Back to the flow", easy_flow.manage_flow_path(@flow), class: "text-sm text-(color:--ks-color-link) hover:text-(color:--ks-color-link-hover) dark:text-(color:--ks-color-link-dark) dark:hover:text-(color:--ks-color-link-hover-dark)" %>
4
4
  </p>
5
5
 
6
- <%= ui_form_page(title: "Edit details", back_url: alembic.manage_flow_path(@diagnostic)) %>
6
+ <%= ui_form_page(title: "Edit details", back_url: easy_flow.manage_flow_path(@flow)) %>
7
7
 
8
- <%= ui_form(action: alembic.manage_flow_path(@diagnostic), method: :patch) do %>
8
+ <%= ui_form(action: easy_flow.manage_flow_path(@flow), method: :patch) do %>
9
9
  <%= ui_section(title: "How this is introduced") do %>
10
- <label class="block text-sm font-medium mb-1">Title</label>
11
- <%= ui_input(name: "flow[title]", value: @diagnostic.title) %>
10
+ <label class="ks-label mb-1">Title</label>
11
+ <%= ui_input(name: "flow[title]", value: @flow.title) %>
12
12
 
13
- <label class="block text-sm font-medium mb-1 mt-4">Summary</label>
14
- <%= ui_textarea(name: "flow[summary]", value: @diagnostic.summary, rows: 4) %>
13
+ <label class="ks-label mb-1 mt-4">Summary</label>
14
+ <%= ui_textarea(name: "flow[summary]", value: @summary, rows: 4) %>
15
15
 
16
- <label class="block text-sm font-medium mb-1 mt-4">Start label</label>
17
- <%= ui_input(name: "flow[start_label]", value: @diagnostic.start_label) %>
16
+ <label class="ks-label mb-1 mt-4">Start label</label>
17
+ <%= ui_input(name: "flow[start_label]", value: @flow.start_label) %>
18
18
  <% end %>
19
19
 
20
20
  <%= ui_section(title: "What this keeps of a run") do %>
21
- <%= ui_select(name: "flow[persists]", selected: @diagnostic.persists,
21
+ <%= ui_select(name: "flow[persists]", selected: @flow.persists,
22
22
  options: [ [ "Nothing", "unsaved" ],
23
23
  [ "Every answer as it is given", "each_step" ],
24
24
  [ "The whole run once it finishes", "on_finish" ] ]) %>
@@ -1 +1 @@
1
- <%= KsBlocks.registry.block_types(kind: :pages).find { |type| type.key.to_s == block["type"] }&.name %>
1
+ <%= shown_block(block) %>