alembic 0.1.0 → 0.3.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 (123) 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 +1 -16
  6. data/app/controllers/alembic/flows_controller.rb +6 -90
  7. data/app/controllers/alembic/manage/base_controller.rb +2 -0
  8. data/app/controllers/alembic/manage/flows_controller.rb +12 -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 +59 -0
  13. data/app/helpers/alembic/application_helper.rb +2 -2
  14. data/app/helpers/alembic/pages_helper.rb +40 -0
  15. data/app/helpers/alembic/result_blocks_helper.rb +45 -0
  16. data/app/models/alembic/flow/definition_summary.rb +10 -0
  17. data/app/models/alembic/flow/run_summary.rb +10 -0
  18. data/app/models/alembic/flow/summaries.rb +44 -5
  19. data/app/models/alembic/flow/summary_version.rb +1 -1
  20. data/app/models/alembic/outputs/band.rb +1 -1
  21. data/app/models/alembic/outputs/grouped.rb +1 -1
  22. data/app/models/alembic/outputs/lowest.rb +5 -3
  23. data/app/models/alembic/outputs/percentage.rb +1 -1
  24. data/app/models/alembic/outputs/weighted_sum.rb +1 -1
  25. data/app/models/alembic/page/drawing.rb +17 -0
  26. data/app/models/alembic/page/options.rb +53 -0
  27. data/app/models/alembic/page/version.rb +13 -0
  28. data/app/models/alembic/page.rb +32 -0
  29. data/app/models/alembic/result_blocks.rb +29 -0
  30. data/app/models/alembic/summary/report.rb +1 -1
  31. data/app/models/alembic/summary/result.rb +1 -1
  32. data/app/views/alembic/blocks/_answers.html.erb +11 -0
  33. data/app/views/alembic/blocks/_band.html.erb +6 -0
  34. data/app/views/alembic/blocks/_categories.html.erb +11 -0
  35. data/app/views/alembic/blocks/_lead.html.erb +15 -0
  36. data/app/views/alembic/blocks/_score.html.erb +7 -0
  37. data/app/views/alembic/blocks/_weakest.html.erb +12 -0
  38. data/app/views/alembic/flows/complete.html.erb +12 -6
  39. data/app/views/alembic/flows/show.html.erb +6 -6
  40. data/app/views/alembic/flows/summary_page.html.erb +5 -0
  41. data/app/views/alembic/manage/flows/edit.html.erb +16 -10
  42. data/app/views/alembic/manage/pages/_block_content.html.erb +1 -1
  43. data/app/views/alembic/manage/pages/index.html.erb +4 -1
  44. data/app/views/alembic/manage/pages/show.html.erb +7 -1
  45. data/app/views/alembic/pages/show.html.erb +5 -0
  46. data/config/routes.rb +8 -19
  47. data/db/migrate/20260825224538_remove_published_version_from_alembic_diagnostics.rb +21 -1
  48. data/db/migrate/20260922120000_add_slug_to_alembic_pages.rb +6 -0
  49. data/db/migrate/20260922130000_create_alembic_page_versions.rb +16 -0
  50. data/db/migrate/20260924220000_move_flows_onto_easy_flow.rb +166 -0
  51. data/db/migrate/20260930120000_add_summary_page_to_alembic_flow_definition_summaries.rb +6 -0
  52. data/lib/alembic/engine.rb +6 -11
  53. data/lib/alembic/version.rb +1 -1
  54. data/lib/alembic.rb +34 -6
  55. metadata +45 -71
  56. data/app/assets/builds/alembic/canvas.js +0 -1
  57. data/app/controllers/alembic/manage/canvas_controller.rb +0 -183
  58. data/app/controllers/alembic/manage/definitions_controller.rb +0 -15
  59. data/app/controllers/alembic/manage/versions_controller.rb +0 -22
  60. data/app/controllers/concerns/alembic/manage/draws_canvas.rb +0 -35
  61. data/app/javascript/alembic/canvas/Canvas.jsx +0 -197
  62. data/app/javascript/alembic/canvas/Connector.jsx +0 -30
  63. data/app/javascript/alembic/canvas/ConnectorLayer.jsx +0 -31
  64. data/app/javascript/alembic/canvas/Control.jsx +0 -42
  65. data/app/javascript/alembic/canvas/Inspector.jsx +0 -44
  66. data/app/javascript/alembic/canvas/Panel.jsx +0 -67
  67. data/app/javascript/alembic/canvas/Placeholder.jsx +0 -24
  68. data/app/javascript/alembic/canvas/Port.jsx +0 -16
  69. data/app/javascript/alembic/canvas/Records.jsx +0 -34
  70. data/app/javascript/alembic/canvas/StepCard.jsx +0 -57
  71. data/app/javascript/alembic/canvas/Toolbar.jsx +0 -19
  72. data/app/javascript/alembic/canvas/TypePicker.jsx +0 -21
  73. data/app/javascript/alembic/canvas/changes.js +0 -5
  74. data/app/javascript/alembic/canvas/choices.js +0 -4
  75. data/app/javascript/alembic/canvas/flow.js +0 -7
  76. data/app/javascript/alembic/canvas/flowSender.js +0 -22
  77. data/app/javascript/alembic/canvas/ids.js +0 -7
  78. data/app/javascript/alembic/canvas/rows.js +0 -2
  79. data/app/javascript/alembic/canvas/styles.js +0 -3
  80. data/app/javascript/alembic/canvas/useConnectors.js +0 -75
  81. data/app/javascript/alembic/canvas/useFlow.js +0 -17
  82. data/app/javascript/alembic/canvas.jsx +0 -13
  83. data/app/models/alembic/asked.rb +0 -3
  84. data/app/models/alembic/choice.rb +0 -7
  85. data/app/models/alembic/flow/admission.rb +0 -19
  86. data/app/models/alembic/flow/canvas.rb +0 -167
  87. data/app/models/alembic/flow/change.rb +0 -18
  88. data/app/models/alembic/flow/condition.rb +0 -24
  89. data/app/models/alembic/flow/definition.rb +0 -132
  90. data/app/models/alembic/flow/digest.rb +0 -116
  91. data/app/models/alembic/flow/document.rb +0 -131
  92. data/app/models/alembic/flow/drawing.rb +0 -15
  93. data/app/models/alembic/flow/edge.rb +0 -9
  94. data/app/models/alembic/flow/edit_history.rb +0 -75
  95. data/app/models/alembic/flow/layout.rb +0 -88
  96. data/app/models/alembic/flow/name.rb +0 -15
  97. data/app/models/alembic/flow/node.rb +0 -5
  98. data/app/models/alembic/flow/output.rb +0 -23
  99. data/app/models/alembic/flow/progress/kept.rb +0 -38
  100. data/app/models/alembic/flow/progress/loose.rb +0 -43
  101. data/app/models/alembic/flow/progress.rb +0 -11
  102. data/app/models/alembic/flow/registry.rb +0 -25
  103. data/app/models/alembic/flow/run.rb +0 -54
  104. data/app/models/alembic/flow/runner.rb +0 -65
  105. data/app/models/alembic/flow/settings.rb +0 -80
  106. data/app/models/alembic/flow/start.rb +0 -13
  107. data/app/models/alembic/flow/step.rb +0 -42
  108. data/app/models/alembic/flow/step_type/declaration.rb +0 -118
  109. data/app/models/alembic/flow/step_type.rb +0 -69
  110. data/app/models/alembic/flow/switch.rb +0 -17
  111. data/app/models/alembic/flow/terminal.rb +0 -13
  112. data/app/models/alembic/flow/validator.rb +0 -171
  113. data/app/models/alembic/flow/version.rb +0 -26
  114. data/app/models/alembic/flow/violation.rb +0 -9
  115. data/app/models/alembic/flow.rb +0 -44
  116. data/app/models/alembic/runner.rb +0 -13
  117. data/app/models/alembic/steps/question.rb +0 -52
  118. data/app/views/alembic/flow/steps/_choosing.html.erb +0 -9
  119. data/app/views/alembic/flows/step.html.erb +0 -27
  120. data/app/views/alembic/manage/definitions/edit.html.erb +0 -13
  121. data/app/views/alembic/manage/flows/index.html.erb +0 -32
  122. data/app/views/alembic/manage/flows/show.html.erb +0 -26
  123. data/app/views/alembic/manage/versions/index.html.erb +0 -41
@@ -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
@@ -0,0 +1,29 @@
1
+ module Alembic
2
+ module ResultBlocks
3
+ OUTPUT = { key: :output, label: "Output" }.freeze
4
+ HEADING = { key: :heading, label: "Heading" }.freeze
5
+
6
+ def self.register
7
+ Page.block(:alembic_score, name: "Score", width: 12, height: 3, drawn_by: :alembic_score_block,
8
+ fields: [ OUTPUT, { key: :kicker, label: "Label above" }, { key: :caption, label: "Caption" } ],
9
+ options: { value: { value_of: :output } })
10
+ Page.block(:alembic_band, name: "Band", width: 12, height: 2, drawn_by: :alembic_band_block,
11
+ fields: [ OUTPUT, { key: :score, label: "Score output" } ],
12
+ options: { value: { value_of: :output }, score: { value_of: :score } })
13
+ Page.block(:alembic_categories, name: "Score per category", width: 12, height: 4, drawn_by: :alembic_categories_block,
14
+ fields: [ OUTPUT, HEADING ], options: { value: { value_of: :output } })
15
+ Page.block(:alembic_weakest, name: "Weakest categories", width: 12, height: 4, drawn_by: :alembic_weakest_block,
16
+ fields: [ OUTPUT, HEADING, { key: :cost_label, label: "Cost label" } ], options: { value: { value_of: :output } })
17
+ Page.block(:alembic_answers, name: "Answers given", width: 12, height: 4, drawn_by: :alembic_answers_block,
18
+ fields: [ HEADING ], options: { value: { value: "answers" } })
19
+ register_lead if Alembic.lead_address
20
+ end
21
+
22
+ def self.register_lead
23
+ Page.block(:alembic_lead, name: "Lead", width: 12, height: 3, drawn_by: :alembic_lead_block,
24
+ fields: [ HEADING, { key: :blurb, label: "Text" }, { key: :button, label: "Button" }, { key: :placeholder, label: "Placeholder" } ],
25
+ options: { slug: { value: "slug" }, note: { value: "note" } })
26
+ end
27
+ private_class_method :register_lead
28
+ end
29
+ end
@@ -13,7 +13,7 @@ module Alembic
13
13
  output_type = @registry.fetch(output["type"])
14
14
  produced[output["id"]] = output_type.compute(output, run, produced)
15
15
 
16
- Result.new(id: output["id"], label: label_for(output, output_type), value: produced[output["id"]])
16
+ Result.new(id: output["id"], type: output["type"], label: label_for(output, output_type), value: produced[output["id"]])
17
17
  end
18
18
  end
19
19
 
@@ -1,5 +1,5 @@
1
1
  module Alembic
2
2
  module Summary
3
- Result = Data.define(:id, :label, :value)
3
+ Result = Data.define(:id, :type, :label, :value)
4
4
  end
5
5
  end
@@ -0,0 +1,11 @@
1
+ <div>
2
+ <h2 class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-4"><%= heading %></h2>
3
+ <ul class="divide-y divide-gray-100 border-y border-gray-100">
4
+ <% answers.each do |question, answer| %>
5
+ <li class="flex items-baseline justify-between gap-6 py-3">
6
+ <span class="text-gray-600"><%= question %></span>
7
+ <span class="shrink-0 font-semibold text-gray-900"><%= answer %></span>
8
+ </li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
@@ -0,0 +1,6 @@
1
+ <div class="text-center">
2
+ <span class="inline-block mt-5 px-4 py-1.5 rounded-full text-sm font-semibold text-white <%= tone %>"><%= band["name"] %></span>
3
+ <% if band["description"].present? %>
4
+ <p class="text-gray-600 leading-relaxed max-w-lg mx-auto mt-4"><%= band["description"] %></p>
5
+ <% end %>
6
+ </div>
@@ -0,0 +1,11 @@
1
+ <div>
2
+ <h2 class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-4"><%= heading %></h2>
3
+ <% shares.each do |name, share| %>
4
+ <div class="flex items-center gap-3 mb-2.5">
5
+ <span class="text-sm text-gray-600 w-44 shrink-0"><%= name %></span>
6
+ <span class="flex-1 h-2 bg-gray-100 rounded-full overflow-hidden">
7
+ <span class="block h-full rounded-full <%= alembic_tone(share) %>" style="width:<%= [ share.to_i, 4 ].max %>%"></span>
8
+ </span>
9
+ </div>
10
+ <% end %>
11
+ </div>
@@ -0,0 +1,15 @@
1
+ <%= ui_panel(padding: :lg) do %>
2
+ <h2 class="text-xl font-semibold text-gray-900 mb-2"><%= heading %></h2>
3
+ <% if blurb.present? %>
4
+ <p class="text-gray-600 mb-5"><%= blurb %></p>
5
+ <% end %>
6
+ <%= form_with url: address, method: :post, class: "flex flex-col sm:flex-row gap-3" do %>
7
+ <%= render Alembic.lead_fields_partial if Alembic.lead_fields_partial %>
8
+ <%= hidden_field_tag "slug", slug %>
9
+ <%= hidden_field_tag "note", note %>
10
+ <div class="flex-1">
11
+ <%= ui_input(name: "email", type: :email, placeholder: placeholder) %>
12
+ </div>
13
+ <button type="submit" class="inline-flex items-center justify-center font-semibold rounded-lg bg-accent-600 text-white hover:bg-accent-500 px-6 py-3 cursor-pointer whitespace-nowrap"><%= button %></button>
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <div class="text-center">
2
+ <p class="text-xs font-semibold uppercase tracking-widest text-accent-600 mb-4"><%= kicker %></p>
3
+ <div class="text-7xl md:text-8xl font-bold tracking-tight text-gray-900"><%= value.nil? ? "—" : "#{value}%" %></div>
4
+ <% if caption.present? %>
5
+ <p class="text-sm text-gray-500 mt-1"><%= caption %></p>
6
+ <% end %>
7
+ </div>
@@ -0,0 +1,12 @@
1
+ <div>
2
+ <h2 class="text-xs font-semibold uppercase tracking-wider text-gray-500 mb-4"><%= heading %></h2>
3
+ <div class="space-y-4">
4
+ <% categories.each do |category| %>
5
+ <%= ui_panel(padding: :lg) do %>
6
+ <h3 class="text-lg font-semibold text-gray-900 mb-1.5"><%= category["name"] %></h3>
7
+ <p class="text-gray-700 mb-2"><%= category["miss"] %></p>
8
+ <p class="text-sm text-accent-700"><span class="font-semibold"><%= cost_label %></span> <%= category["cost"] %></p>
9
+ <% end %>
10
+ <% end %>
11
+ </div>
12
+ </div>
@@ -11,22 +11,28 @@
11
11
 
12
12
  <% if @outputs.any? %>
13
13
  <%= ui_section do %>
14
- <%= ui_grid(cols: { default: 1, sm: 2 }, gap: :lg) do %>
14
+ <div class="space-y-10">
15
15
  <% @outputs.each do |output| %>
16
16
  <div data-output="<%= output.id %>">
17
- <%= ui_stat_card(label: output.label, value: alembic_output_lines(output.value).join(" · ")) %>
17
+ <%= alembic_default_result(output, @outputs) %>
18
18
  </div>
19
19
  <% end %>
20
- <% end %>
20
+ </div>
21
+ <% end %>
22
+ <% end %>
23
+
24
+ <% if Alembic.lead_address %>
25
+ <%= ui_section do %>
26
+ <%= alembic_lead_block(slug: summary_values["slug"], note: summary_values["note"]) %>
21
27
  <% end %>
22
28
  <% end %>
23
29
 
24
30
  <%= ui_section(title: @outputs.any? ? "What you told us" : nil) do %>
25
- <ul class="divide-y divide-gray-100 border-y border-gray-100">
31
+ <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
32
  <% @answered.each do |step, value| %>
27
33
  <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>
34
+ <span class="ks-tone-muted"><%= @guide.question_text(step) %></span>
35
+ <span class="shrink-0 font-(weight:--ks-font-weight-medium) ks-tone-neutral"><%= @guide.choice_label(step, value) %></span>
30
36
  </li>
31
37
  <% end %>
32
38
  </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 %>
@@ -0,0 +1,5 @@
1
+ <% content_for :head do %>
2
+ <%= stylesheet_link_tag "ks_blocks" %>
3
+ <% end %>
4
+
5
+ <%= drawn_page(@summary_page, values: summary_values) %>
@@ -1,24 +1,30 @@
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
+ <% end %>
19
+
20
+ <%= ui_section(title: "Where this finishes") do %>
21
+ <%= ui_select(name: "flow[summary_page_id]", selected: @summary_page&.id,
22
+ include_blank: "Alembic's default summary page",
23
+ options: @published_pages.map { |page| [ page.name, page.id ] }) %>
18
24
  <% end %>
19
25
 
20
26
  <%= ui_section(title: "What this keeps of a run") do %>
21
- <%= ui_select(name: "flow[persists]", selected: @diagnostic.persists,
27
+ <%= ui_select(name: "flow[persists]", selected: @flow.persists,
22
28
  options: [ [ "Nothing", "unsaved" ],
23
29
  [ "Every answer as it is given", "each_step" ],
24
30
  [ "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) %>
@@ -11,9 +11,12 @@
11
11
 
12
12
  <%= ui_section(title: "Create a page") do %>
13
13
  <%= ui_form(action: alembic.manage_pages_path, method: :post) do %>
14
- <label class="block text-sm font-medium mb-1">Name</label>
14
+ <label class="ks-label mb-1">Name</label>
15
15
  <%= ui_input(name: "page[name]", placeholder: "e.g. Welcome") %>
16
16
 
17
+ <label class="ks-label mb-1">Address</label>
18
+ <%= ui_input(name: "page[slug]", placeholder: "e.g. welcome") %>
19
+
17
20
  <%= ui_button(label: "Create", type: :submit, variant: :secondary) %>
18
21
  <% end %>
19
22
  <% end %>
@@ -4,7 +4,13 @@
4
4
  <%= javascript_include_tag "alembic/page_builder", defer: true %>
5
5
  <% end %>
6
6
 
7
- <%= react_ui("alembic/page-builder", @payload.merge(token: form_authenticity_token)) %>
7
+ <%= ui_form(action: alembic.publish_manage_page_path(@page), method: :post) do %>
8
+ <%= ui_button(label: "Publish", type: :submit, variant: :secondary) %>
9
+ <% end %>
10
+
11
+ <%= ui_panel do %>
12
+ <%= react_ui("alembic/page-builder", @payload.merge(token: form_authenticity_token)) %>
13
+ <% end %>
8
14
 
9
15
  <%= block_contents(@page.blocks) do |block| %>
10
16
  <%= render "alembic/manage/pages/block_content", block: block %>
@@ -0,0 +1,5 @@
1
+ <% content_for :head do %>
2
+ <%= stylesheet_link_tag "ks_blocks" %>
3
+ <% end %>
4
+
5
+ <%= drawn_page(@page) %>
data/config/routes.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  Alembic::Engine.routes.draw do
2
2
  namespace :manage do
3
3
  resources :pages, only: [ :index, :create, :show ] do
4
+ post "publish", on: :member
4
5
  get "layout", to: "page_blocks#layout", as: :layout
5
6
  post "blocks", to: "page_blocks#add_block", as: :blocks
6
7
  patch "blocks", to: "page_blocks#place_blocks"
@@ -8,35 +9,23 @@ Alembic::Engine.routes.draw do
8
9
  delete "blocks/:block_id", to: "page_blocks#remove_block", as: :block
9
10
  end
10
11
 
11
- resources :flows, only: [ :index, :create, :show, :edit, :update, :destroy ] do
12
- resource :definition, only: [ :edit, :update ]
12
+ resources :flows, only: [ :edit, :update ] do
13
13
  resource :preview, only: :show, controller: "previews" do
14
14
  get :step
15
15
  end
16
- resources :versions, only: :index do
17
- post :return, on: :member
18
- end
19
-
20
- resource :canvas, only: :show, controller: "canvas" do
21
- post "steps", action: :add_step
22
- patch "steps/:step", action: :configure_step
23
- delete "steps/:step", action: :remove_step
24
- patch "steps/:step/move", action: :move_step
25
- post "versions", action: :create
26
- post "publish", action: :publish
27
- patch "details", action: :details
28
- post "undo", action: :undo
29
- post "redo", action: :redo
30
- post "edges", action: :connect
31
- delete "edges", action: :disconnect
32
- end
33
16
  end
34
17
  end
35
18
 
19
+ constraints(->(request) { request.path_info.match?(%r{\A/manage/flows(/|\z)}) }) do
20
+ mount EasyFlow::Engine, at: "/", defaults: { easy_flow_host: Alembic::FLOW_HOST.to_s }
21
+ end
22
+
36
23
  post ":slug/runs", to: "flows#start", as: :flow_runs
37
24
  get "runs/:id", to: "flows#step", as: :run
38
25
  patch "runs/:id", to: "flows#update"
39
26
 
27
+ get "pages/:slug", to: "pages#show", as: :page
28
+
40
29
  get ":slug", to: "flows#show", as: :flow
41
30
  get ":slug/step", to: "flows#step", as: :flow_step
42
31
  end
@@ -1,5 +1,25 @@
1
1
  class RemovePublishedVersionFromAlembicDiagnostics < ActiveRecord::Migration[8.1]
2
- def change
2
+ class Diagnostic < ActiveRecord::Base
3
+ self.table_name = "alembic_diagnostics"
4
+ end
5
+
6
+ class DefinitionVersion < ActiveRecord::Base
7
+ self.table_name = "alembic_definition_versions"
8
+ end
9
+
10
+ def up
11
+ [ Diagnostic, DefinitionVersion ].each(&:reset_column_information)
12
+ DefinitionVersion.where(id: Diagnostic.where.not(published_version_id: nil).select(:published_version_id)).update_all(status: "live")
13
+
3
14
  remove_reference :alembic_diagnostics, :published_version, foreign_key: { to_table: :alembic_definition_versions }
4
15
  end
16
+
17
+ def down
18
+ add_reference :alembic_diagnostics, :published_version, foreign_key: { to_table: :alembic_definition_versions }
19
+
20
+ [ Diagnostic, DefinitionVersion ].each(&:reset_column_information)
21
+ DefinitionVersion.where(status: "live").find_each do |version|
22
+ Diagnostic.where(id: version.diagnostic_id).update_all(published_version_id: version.id)
23
+ end
24
+ end
5
25
  end
@@ -0,0 +1,6 @@
1
+ class AddSlugToAlembicPages < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :alembic_pages, :slug, :string
4
+ add_index :alembic_pages, :slug, unique: true
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ class CreateAlembicPageVersions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_page_versions do |t|
4
+ t.references :page, null: false, foreign_key: { to_table: :alembic_pages }
5
+ t.integer :number, null: false
6
+ t.json :blocks, default: [], null: false
7
+ t.string :status, null: false, default: "draft"
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :alembic_page_versions, [ :page_id, :number ], unique: true
13
+ add_index :alembic_page_versions, :page_id, unique: true, where: "status = 'live'",
14
+ name: "index_alembic_page_versions_on_one_live_per_page"
15
+ end
16
+ end
@@ -0,0 +1,166 @@
1
+ class MoveFlowsOntoEasyFlow < ActiveRecord::Migration[8.1]
2
+ HOST = "alembic"
3
+ FLOW_COLUMNS = %w[slug title start_label kind status persists document definition_cursor
4
+ changes_since_version undo_history undone_changes created_at updated_at].freeze
5
+
6
+ class OldFlow < ActiveRecord::Base
7
+ self.table_name = "alembic_flows"
8
+ end
9
+
10
+ class Definition < ActiveRecord::Base
11
+ self.table_name = "easy_flow_definitions"
12
+ end
13
+
14
+ class OldVersion < ActiveRecord::Base
15
+ self.table_name = "alembic_flow_versions"
16
+ end
17
+
18
+ class Version < ActiveRecord::Base
19
+ self.table_name = "easy_flow_versions"
20
+ end
21
+
22
+ class OldRun < ActiveRecord::Base
23
+ self.table_name = "alembic_flow_runs"
24
+ end
25
+
26
+ class Run < ActiveRecord::Base
27
+ self.table_name = "easy_flow_runs"
28
+ end
29
+
30
+ class SummaryVersion < ActiveRecord::Base
31
+ self.table_name = "alembic_flow_summaries"
32
+ end
33
+
34
+ class DefinitionSummary < ActiveRecord::Base
35
+ self.table_name = "alembic_flow_definition_summaries"
36
+ end
37
+
38
+ class RunSummary < ActiveRecord::Base
39
+ self.table_name = "alembic_flow_run_summaries"
40
+ end
41
+
42
+ def up
43
+ [ OldFlow, Definition, OldVersion, Version, OldRun, Run, SummaryVersion ].each(&:reset_column_information)
44
+ flows = copy_flows
45
+ versions = copy_versions(flows)
46
+ runs = copy_runs(flows, versions)
47
+ summaries = OldFlow.pluck(:id, :summary, :summary_cursor)
48
+ pins = OldRun.where.not(summary_version_id: nil).pluck(:id, :summary_version_id)
49
+
50
+ drop_table :alembic_flow_runs
51
+ remove_foreign_key :alembic_flow_summaries, column: :flow_id
52
+ repoint_summary_versions(flows)
53
+ drop_table :alembic_flow_versions
54
+ drop_table :alembic_flows
55
+ add_foreign_key :alembic_flow_summaries, :easy_flow_definitions, column: :flow_id, on_delete: :cascade
56
+
57
+ create_table :alembic_flow_definition_summaries do |t|
58
+ t.references :flow, null: false, index: { unique: true },
59
+ foreign_key: { to_table: :easy_flow_definitions, on_delete: :cascade }
60
+ t.text :summary
61
+ t.integer :summary_cursor
62
+ t.timestamps
63
+ end
64
+ restore_summaries(summaries, flows)
65
+
66
+ create_table :alembic_flow_run_summaries do |t|
67
+ t.references :run, null: false, index: { unique: true },
68
+ foreign_key: { to_table: :easy_flow_runs, on_delete: :cascade }
69
+ t.references :summary_version, null: false,
70
+ foreign_key: { to_table: :alembic_flow_summaries, on_delete: :cascade }
71
+ t.timestamps
72
+ end
73
+ restore_pins(pins, runs)
74
+ end
75
+
76
+ def down
77
+ drop_table :alembic_flow_run_summaries
78
+ drop_table :alembic_flow_definition_summaries
79
+ remove_foreign_key :alembic_flow_summaries, column: :flow_id
80
+ execute "DELETE FROM alembic_flow_summaries"
81
+
82
+ create_table :alembic_flows do |t|
83
+ t.string :slug
84
+ t.string :title
85
+ t.text :summary
86
+ t.string :start_label
87
+ t.string :kind
88
+ t.string :status, null: false, default: "active"
89
+ t.string :persists, null: false, default: "unsaved"
90
+ t.json :document
91
+ t.integer :definition_cursor
92
+ t.integer :summary_cursor
93
+ t.json :changes_since_version
94
+ t.json :undo_history
95
+ t.json :undone_changes
96
+ t.timestamps
97
+ end
98
+ add_index :alembic_flows, :slug, unique: true
99
+
100
+ create_table :alembic_flow_versions do |t|
101
+ t.references :flow, null: false, foreign_key: { to_table: :alembic_flows }
102
+ t.integer :number, null: false
103
+ t.json :definition
104
+ t.json :changes_captured
105
+ t.string :status, null: false, default: "draft"
106
+ t.datetime :created_at, null: false
107
+ end
108
+ add_index :alembic_flow_versions, [ :flow_id, :number ], unique: true
109
+ add_index :alembic_flow_versions, :flow_id, unique: true, where: "status = 'live'",
110
+ name: "index_alembic_flow_versions_on_one_live_per_flow"
111
+
112
+ add_foreign_key :alembic_flow_summaries, :alembic_flows, column: :flow_id
113
+
114
+ create_table :alembic_flow_runs do |t|
115
+ t.references :flow, null: false, foreign_key: { to_table: :alembic_flows }
116
+ t.references :definition_version, null: false, foreign_key: { to_table: :alembic_flow_versions }
117
+ t.references :summary_version, foreign_key: { to_table: :alembic_flow_summaries }
118
+ t.references :owner, polymorphic: true
119
+ t.json :recorded
120
+ t.string :label
121
+ t.string :status
122
+ t.timestamps
123
+ end
124
+ end
125
+
126
+ private
127
+
128
+ def copy_flows
129
+ OldFlow.order(:id).to_h do |flow|
130
+ [ flow.id, Definition.create!(flow.attributes.slice(*FLOW_COLUMNS).merge("host" => HOST)).id ]
131
+ end
132
+ end
133
+
134
+ def copy_versions(flows)
135
+ OldVersion.order(:id).to_h do |version|
136
+ copied = version.attributes.slice("number", "definition", "changes_captured", "status", "created_at")
137
+ [ version.id, Version.create!(copied.merge("flow_id" => flows.fetch(version.flow_id))).id ]
138
+ end
139
+ end
140
+
141
+ def copy_runs(flows, versions)
142
+ OldRun.order(:id).to_h do |run|
143
+ copied = run.attributes.slice("owner_type", "owner_id", "recorded", "label", "status", "created_at", "updated_at")
144
+ moved = copied.merge("flow_id" => flows.fetch(run.flow_id), "definition_version_id" => versions.fetch(run.definition_version_id))
145
+ [ run.id, Run.create!(moved).id ]
146
+ end
147
+ end
148
+
149
+ def restore_summaries(summaries, flows)
150
+ DefinitionSummary.reset_column_information
151
+ summaries.each do |flow_id, summary, cursor|
152
+ next if summary.nil? && cursor.nil?
153
+
154
+ DefinitionSummary.create!(flow_id: flows.fetch(flow_id), summary: summary, summary_cursor: cursor)
155
+ end
156
+ end
157
+
158
+ def restore_pins(pins, runs)
159
+ RunSummary.reset_column_information
160
+ pins.each { |run_id, summary_version_id| RunSummary.create!(run_id: runs.fetch(run_id), summary_version_id: summary_version_id) }
161
+ end
162
+
163
+ def repoint_summary_versions(flows)
164
+ SummaryVersion.find_each { |version| version.update_columns(flow_id: flows.fetch(version.flow_id)) }
165
+ end
166
+ end
@@ -0,0 +1,6 @@
1
+ class AddSummaryPageToAlembicFlowDefinitionSummaries < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_reference :alembic_flow_definition_summaries, :summary_page,
4
+ foreign_key: { to_table: :alembic_pages, on_delete: :nullify }
5
+ end
6
+ end
@@ -1,6 +1,7 @@
1
1
  require "alembic/leftover_stylesheet"
2
2
  require "keystone_ui-blocks"
3
3
  require "keystone_ui-react"
4
+ require "easy_flow"
4
5
 
5
6
  module Alembic
6
7
  class Engine < ::Rails::Engine
@@ -19,18 +20,8 @@ module Alembic
19
20
  KeystoneUi.configuration.tailwind_sources << root.join("app/assets/builds/alembic/*.js").to_s
20
21
  end
21
22
 
22
- initializer "alembic.step_types" do |app|
23
+ initializer "alembic.output_types" do |app|
23
24
  app.config.to_prepare do
24
- Alembic::Flow::Start.register
25
- Alembic::Flow::Terminal.register
26
-
27
- Alembic::Steps::Question.register
28
- Alembic::Flow::Condition.register
29
- Alembic::Flow::Switch.register
30
-
31
- Alembic::Flow.check(:unrouted_value)
32
- Alembic::Flow.check(:unfollowed_path)
33
- Alembic::Flow.check(:dead_end)
34
25
  Alembic::Outputs::WeightedSum.register
35
26
  Alembic::Outputs::Percentage.register
36
27
  Alembic::Outputs::Grouped.register
@@ -39,5 +30,9 @@ module Alembic
39
30
  Alembic::Outputs::Band.register
40
31
  end
41
32
  end
33
+
34
+ initializer "alembic.result_blocks" do |app|
35
+ app.config.to_prepare { Alembic::ResultBlocks.register }
36
+ end
42
37
  end
43
38
  end
@@ -1,3 +1,3 @@
1
1
  module Alembic
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end