alembic 0.1.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 (168) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +105 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/builds/alembic/canvas.js +1 -0
  6. data/app/assets/builds/alembic/page_builder.css +1 -0
  7. data/app/assets/builds/alembic/page_builder.js +5 -0
  8. data/app/assets/stylesheets/alembic/application.css +15 -0
  9. data/app/controllers/alembic/application_controller.rb +32 -0
  10. data/app/controllers/alembic/flows_controller.rb +104 -0
  11. data/app/controllers/alembic/manage/base_controller.rb +16 -0
  12. data/app/controllers/alembic/manage/canvas_controller.rb +183 -0
  13. data/app/controllers/alembic/manage/definitions_controller.rb +15 -0
  14. data/app/controllers/alembic/manage/flows_controller.rb +55 -0
  15. data/app/controllers/alembic/manage/page_blocks_controller.rb +19 -0
  16. data/app/controllers/alembic/manage/pages_controller.rb +38 -0
  17. data/app/controllers/alembic/manage/previews_controller.rb +38 -0
  18. data/app/controllers/alembic/manage/versions_controller.rb +22 -0
  19. data/app/controllers/concerns/alembic/manage/authenticates_admin.rb +19 -0
  20. data/app/controllers/concerns/alembic/manage/draws_canvas.rb +35 -0
  21. data/app/helpers/alembic/application_helper.rb +11 -0
  22. data/app/javascript/alembic/canvas/Canvas.jsx +197 -0
  23. data/app/javascript/alembic/canvas/Connector.jsx +30 -0
  24. data/app/javascript/alembic/canvas/ConnectorLayer.jsx +31 -0
  25. data/app/javascript/alembic/canvas/Control.jsx +42 -0
  26. data/app/javascript/alembic/canvas/Inspector.jsx +44 -0
  27. data/app/javascript/alembic/canvas/Panel.jsx +67 -0
  28. data/app/javascript/alembic/canvas/Placeholder.jsx +24 -0
  29. data/app/javascript/alembic/canvas/Port.jsx +16 -0
  30. data/app/javascript/alembic/canvas/Records.jsx +34 -0
  31. data/app/javascript/alembic/canvas/StepCard.jsx +57 -0
  32. data/app/javascript/alembic/canvas/Toolbar.jsx +19 -0
  33. data/app/javascript/alembic/canvas/TypePicker.jsx +21 -0
  34. data/app/javascript/alembic/canvas/changes.js +5 -0
  35. data/app/javascript/alembic/canvas/choices.js +4 -0
  36. data/app/javascript/alembic/canvas/flow.js +7 -0
  37. data/app/javascript/alembic/canvas/flowSender.js +22 -0
  38. data/app/javascript/alembic/canvas/ids.js +7 -0
  39. data/app/javascript/alembic/canvas/rows.js +2 -0
  40. data/app/javascript/alembic/canvas/styles.js +3 -0
  41. data/app/javascript/alembic/canvas/useConnectors.js +75 -0
  42. data/app/javascript/alembic/canvas/useFlow.js +17 -0
  43. data/app/javascript/alembic/canvas.jsx +13 -0
  44. data/app/javascript/alembic/page_builder/PageBuilder.jsx +18 -0
  45. data/app/javascript/alembic/page_builder.jsx +10 -0
  46. data/app/jobs/alembic/application_job.rb +4 -0
  47. data/app/mailers/alembic/application_mailer.rb +6 -0
  48. data/app/models/alembic/application_record.rb +5 -0
  49. data/app/models/alembic/asked.rb +3 -0
  50. data/app/models/alembic/choice.rb +7 -0
  51. data/app/models/alembic/flow/admission.rb +19 -0
  52. data/app/models/alembic/flow/canvas.rb +167 -0
  53. data/app/models/alembic/flow/change.rb +18 -0
  54. data/app/models/alembic/flow/condition.rb +24 -0
  55. data/app/models/alembic/flow/definition.rb +132 -0
  56. data/app/models/alembic/flow/digest.rb +116 -0
  57. data/app/models/alembic/flow/document.rb +131 -0
  58. data/app/models/alembic/flow/drawing.rb +15 -0
  59. data/app/models/alembic/flow/edge.rb +9 -0
  60. data/app/models/alembic/flow/edit_history.rb +75 -0
  61. data/app/models/alembic/flow/layout.rb +88 -0
  62. data/app/models/alembic/flow/name.rb +15 -0
  63. data/app/models/alembic/flow/node.rb +5 -0
  64. data/app/models/alembic/flow/output.rb +23 -0
  65. data/app/models/alembic/flow/progress/kept.rb +38 -0
  66. data/app/models/alembic/flow/progress/loose.rb +43 -0
  67. data/app/models/alembic/flow/progress.rb +11 -0
  68. data/app/models/alembic/flow/registry.rb +25 -0
  69. data/app/models/alembic/flow/run.rb +54 -0
  70. data/app/models/alembic/flow/runner.rb +65 -0
  71. data/app/models/alembic/flow/settings.rb +80 -0
  72. data/app/models/alembic/flow/start.rb +13 -0
  73. data/app/models/alembic/flow/step.rb +42 -0
  74. data/app/models/alembic/flow/step_type/declaration.rb +118 -0
  75. data/app/models/alembic/flow/step_type.rb +69 -0
  76. data/app/models/alembic/flow/summaries.rb +44 -0
  77. data/app/models/alembic/flow/summary_version.rb +13 -0
  78. data/app/models/alembic/flow/switch.rb +17 -0
  79. data/app/models/alembic/flow/terminal.rb +13 -0
  80. data/app/models/alembic/flow/validator.rb +171 -0
  81. data/app/models/alembic/flow/version.rb +26 -0
  82. data/app/models/alembic/flow/violation.rb +9 -0
  83. data/app/models/alembic/flow.rb +44 -0
  84. data/app/models/alembic/outputs/band.rb +22 -0
  85. data/app/models/alembic/outputs/grouped.rb +26 -0
  86. data/app/models/alembic/outputs/lowest.rb +20 -0
  87. data/app/models/alembic/outputs/percentage.rb +31 -0
  88. data/app/models/alembic/outputs/tally.rb +23 -0
  89. data/app/models/alembic/outputs/weighted_sum.rb +26 -0
  90. data/app/models/alembic/page.rb +11 -0
  91. data/app/models/alembic/runner.rb +13 -0
  92. data/app/models/alembic/steps/question.rb +52 -0
  93. data/app/models/alembic/summary/output_type.rb +40 -0
  94. data/app/models/alembic/summary/registry.rb +25 -0
  95. data/app/models/alembic/summary/report.rb +31 -0
  96. data/app/models/alembic/summary/result.rb +5 -0
  97. data/app/models/alembic/summary/run.rb +13 -0
  98. data/app/models/alembic/summary.rb +15 -0
  99. data/app/views/alembic/flow/steps/_choosing.html.erb +9 -0
  100. data/app/views/alembic/flows/complete.html.erb +34 -0
  101. data/app/views/alembic/flows/show.html.erb +17 -0
  102. data/app/views/alembic/flows/step.html.erb +27 -0
  103. data/app/views/alembic/manage/definitions/edit.html.erb +13 -0
  104. data/app/views/alembic/manage/flows/edit.html.erb +29 -0
  105. data/app/views/alembic/manage/flows/index.html.erb +32 -0
  106. data/app/views/alembic/manage/flows/show.html.erb +26 -0
  107. data/app/views/alembic/manage/pages/_block_content.html.erb +1 -0
  108. data/app/views/alembic/manage/pages/index.html.erb +20 -0
  109. data/app/views/alembic/manage/pages/show.html.erb +11 -0
  110. data/app/views/alembic/manage/versions/index.html.erb +41 -0
  111. data/app/views/layouts/alembic/application.html.erb +17 -0
  112. data/config/routes.rb +42 -0
  113. data/db/migrate/20260604215031_create_alembic_diagnostics.rb +14 -0
  114. data/db/migrate/20260605015051_create_alembic_questions.rb +12 -0
  115. data/db/migrate/20260605021526_create_alembic_options.rb +14 -0
  116. data/db/migrate/20260605023308_create_alembic_conditions.rb +11 -0
  117. data/db/migrate/20260605025623_create_alembic_bands.rb +12 -0
  118. data/db/migrate/20260605032859_create_alembic_condition_options.rb +10 -0
  119. data/db/migrate/20260605032900_reshape_conditions_for_placement.rb +11 -0
  120. data/db/migrate/20260605125119_create_alembic_results.rb +14 -0
  121. data/db/migrate/20260605130338_create_alembic_rules.rb +10 -0
  122. data/db/migrate/20260605130339_create_alembic_rule_results.rb +10 -0
  123. data/db/migrate/20260605210714_add_definition_to_alembic_diagnostics.rb +5 -0
  124. data/db/migrate/20260606000855_add_guide_copy_to_alembic_diagnostics.rb +9 -0
  125. data/db/migrate/20260606005430_create_alembic_nodes.rb +22 -0
  126. data/db/migrate/20260606010312_create_alembic_build_steps.rb +12 -0
  127. data/db/migrate/20260606011110_create_alembic_warnings.rb +11 -0
  128. data/db/migrate/20260804180000_create_alembic_domains.rb +14 -0
  129. data/db/migrate/20260804180100_add_domain_to_alembic_questions.rb +5 -0
  130. data/db/migrate/20260822120000_create_alembic_definition_versions.rb +14 -0
  131. data/db/migrate/20260822120100_backfill_alembic_definition_versions.rb +17 -0
  132. data/db/migrate/20260822130000_remove_definition_from_alembic_diagnostics.rb +28 -0
  133. data/db/migrate/20260822140000_create_alembic_responses.rb +14 -0
  134. data/db/migrate/20260824120000_create_alembic_transitions.rb +10 -0
  135. data/db/migrate/20260824130000_drop_alembic_builder_tables.rb +17 -0
  136. data/db/migrate/20260824140000_add_definition_cursor_to_alembic_diagnostics.rb +5 -0
  137. data/db/migrate/20260824160000_add_summary_definition_to_alembic_diagnostics.rb +5 -0
  138. data/db/migrate/20260824170000_drop_alembic_guide_copy_columns.rb +8 -0
  139. data/db/migrate/20260825120000_create_alembic_summary_versions.rb +14 -0
  140. data/db/migrate/20260825130000_add_summary_cursor_to_alembic_diagnostics.rb +5 -0
  141. data/db/migrate/20260825140000_move_summary_definitions_into_versions.rb +26 -0
  142. data/db/migrate/20260825150000_drop_summary_definition_from_alembic_diagnostics.rb +5 -0
  143. data/db/migrate/20260825160000_add_summary_version_to_alembic_responses.rb +6 -0
  144. data/db/migrate/20260825170000_rename_question_settings_in_current_definitions.rb +51 -0
  145. data/db/migrate/20260825180000_add_document_to_alembic_diagnostics.rb +6 -0
  146. data/db/migrate/20260825190000_seed_documents_from_current_definitions.rb +29 -0
  147. data/db/migrate/20260825200000_add_published_version_to_alembic_diagnostics.rb +6 -0
  148. data/db/migrate/20260825210000_publish_current_definitions.rb +29 -0
  149. data/db/migrate/20260825220000_add_undone_changes_to_alembic_diagnostics.rb +5 -0
  150. data/db/migrate/20260825223322_add_status_to_alembic_definition_versions.rb +8 -0
  151. data/db/migrate/20260825224538_remove_published_version_from_alembic_diagnostics.rb +5 -0
  152. data/db/migrate/20260825225015_change_alembic_diagnostic_status_to_availability.rb +16 -0
  153. data/db/migrate/20260825230000_add_undo_history_to_alembic_diagnostics.rb +5 -0
  154. data/db/migrate/20260825230500_add_changes_to_alembic_definition_versions.rb +5 -0
  155. data/db/migrate/20260826230000_rename_alembic_responses_to_flow_runs.rb +5 -0
  156. data/db/migrate/20260826234500_rename_answers_to_recorded_on_flow_runs.rb +5 -0
  157. data/db/migrate/20260826235000_rename_definition_versions_to_flow_versions.rb +5 -0
  158. data/db/migrate/20260826235500_rename_summary_versions_to_flow_summaries.rb +5 -0
  159. data/db/migrate/20260827000000_rename_diagnostics_to_flows.rb +8 -0
  160. data/db/migrate/20260831000000_add_persists_to_alembic_flows.rb +5 -0
  161. data/db/migrate/20260914120000_create_alembic_pages.rb +9 -0
  162. data/db/migrate/20260915120000_add_blocks_to_alembic_pages.rb +5 -0
  163. data/lib/alembic/engine.rb +43 -0
  164. data/lib/alembic/leftover_stylesheet.rb +14 -0
  165. data/lib/alembic/version.rb +3 -0
  166. data/lib/alembic.rb +34 -0
  167. data/lib/tasks/alembic_tasks.rake +4 -0
  168. metadata +252 -0
@@ -0,0 +1,13 @@
1
+ module Alembic
2
+ module Summary
3
+ Run = Data.define(:state, :steps) do
4
+ def initialize(state:, steps: {})
5
+ super
6
+ end
7
+
8
+ def step(id)
9
+ steps[id.to_s].to_h
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module Alembic
2
+ module Summary
3
+ class UnknownOutputType < KeyError; end
4
+
5
+ class << self
6
+ def output(id, &declaration)
7
+ registry.register(OutputType.define(id, &declaration))
8
+ end
9
+
10
+ def registry
11
+ @registry ||= Registry.new
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ <fieldset>
2
+ <legend class="mb-5 text-xl font-semibold leading-snug text-gray-900"><%= step.text %></legend>
3
+
4
+ <div class="space-y-3">
5
+ <% step.choices.each do |choice| %>
6
+ <%= ui_radio_card(name: "answers[#{step.id}]", value: choice.value, label: choice.label.presence || choice.value, hint: choice.hint) %>
7
+ <% end %>
8
+ </div>
9
+ </fieldset>
@@ -0,0 +1,34 @@
1
+ <% content_for(:title, "Complete") %>
2
+
3
+ <% starting_over = flow_start_path(@guide.slug) %>
4
+
5
+ <%= ui_page(max_width: :md) do %>
6
+ <%= ui_page_header(title: "That's everything", subtitle: "Thanks — here's what you told us") do |header| %>
7
+ <% header.action do %>
8
+ <%= ui_button(label: "Start over", href: starting_over, variant: :secondary) %>
9
+ <% end %>
10
+ <% end %>
11
+
12
+ <% if @outputs.any? %>
13
+ <%= ui_section do %>
14
+ <%= ui_grid(cols: { default: 1, sm: 2 }, gap: :lg) do %>
15
+ <% @outputs.each do |output| %>
16
+ <div data-output="<%= output.id %>">
17
+ <%= ui_stat_card(label: output.label, value: alembic_output_lines(output.value).join(" · ")) %>
18
+ </div>
19
+ <% end %>
20
+ <% end %>
21
+ <% end %>
22
+ <% end %>
23
+
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">
26
+ <% @answered.each do |step, value| %>
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>
30
+ </li>
31
+ <% end %>
32
+ </ul>
33
+ <% end %>
34
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <% content_for(:title, @diagnostic.title.presence || @diagnostic.slug) %>
2
+
3
+ <%= ui_page(max_width: :md) do %>
4
+ <%= ui_page_header(title: @diagnostic.title.presence || @diagnostic.slug, subtitle: @diagnostic.summary) %>
5
+
6
+ <%= ui_section do %>
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) %>
11
+ <% end %>
12
+ <% else %>
13
+ <%= ui_button(label: @diagnostic.start_label.presence || "Start", href: flow_step_path(@diagnostic.slug)) %>
14
+ <% end %>
15
+ </div>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,27 @@
1
+ <% content_for(:title, @guide.headline) %>
2
+
3
+ <%= ui_page(max_width: :md) do %>
4
+ <%= link_to "← Start over", flow_start_path(@guide.slug), class: "text-sm text-accent-600 hover:text-accent-700" %>
5
+
6
+ <div class="mt-6 mb-6">
7
+ <%= ui_progress(value: @answers.size, max: [ @guide.steps_on_path(@answers).size, 1 ].max, label: "Question #{@answers.size + 1}") %>
8
+ </div>
9
+
10
+ <%= form_with url: step_form[:url], method: step_form[:method], data: { turbo: false } do %>
11
+ <% if carries_answers? %>
12
+ <% @answers.each do |key, value| %>
13
+ <%= hidden_field_tag "answers[#{key}]", value %>
14
+ <% end %>
15
+ <% end %>
16
+
17
+ <%= render @drawing, step: @question %>
18
+
19
+ <div class="mt-8 flex items-center gap-4">
20
+ <%= ui_button(label: "Next", type: :submit) %>
21
+
22
+ <% if @answers.any? %>
23
+ <button type="submit" name="back" value="1" formnovalidate class="cursor-pointer text-sm text-gray-500 underline underline-offset-2 hover:text-gray-900">← Back</button>
24
+ <% end %>
25
+ </div>
26
+ <% end %>
27
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <%= ui_page(max_width: :lg) do %>
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" %>
4
+ </p>
5
+
6
+ <%= ui_form_page(title: "Definition", back_url: alembic.manage_flow_path(@diagnostic), subtitle: @diagnostic.slug) %>
7
+
8
+ <%= ui_form(action: alembic.manage_flow_definition_path(@diagnostic), method: :patch) do %>
9
+ <%= ui_textarea(name: "definition", value: JSON.pretty_generate(@diagnostic.document), rows: 30) %>
10
+
11
+ <%= ui_button(label: "Save", type: :submit) %>
12
+ <% end %>
13
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <%= ui_page(max_width: :lg) do %>
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" %>
4
+ </p>
5
+
6
+ <%= ui_form_page(title: "Edit details", back_url: alembic.manage_flow_path(@diagnostic)) %>
7
+
8
+ <%= ui_form(action: alembic.manage_flow_path(@diagnostic), method: :patch) do %>
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) %>
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) %>
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) %>
18
+ <% end %>
19
+
20
+ <%= ui_section(title: "What this keeps of a run") do %>
21
+ <%= ui_select(name: "flow[persists]", selected: @diagnostic.persists,
22
+ options: [ [ "Nothing", "unsaved" ],
23
+ [ "Every answer as it is given", "each_step" ],
24
+ [ "The whole run once it finishes", "on_finish" ] ]) %>
25
+ <% end %>
26
+
27
+ <%= ui_button(label: "Save", type: :submit) %>
28
+ <% end %>
29
+ <% end %>
@@ -0,0 +1,32 @@
1
+ <%= ui_page(max_width: :xl) do %>
2
+ <%= ui_page_header(title: "Flows", subtitle: "Build and edit your flows") %>
3
+
4
+ <%= ui_data_table(
5
+ items: @diagnostics,
6
+ columns: [ { title: "Title" }, { slug: "Slug" }, { kind: "Kind" }, { status: "Status" } ],
7
+ empty_message: "No diagnostics yet."
8
+ ) do |table| %>
9
+ <% table.link(:title) { |diagnostic| alembic.manage_flow_path(diagnostic) } %>
10
+ <% table.actions do |diagnostic| %>
11
+ <%= ui_form(action: alembic.manage_flow_path(diagnostic), method: :delete) do %>
12
+ <%= ui_button(label: "Remove", type: :submit, variant: :secondary) %>
13
+ <% end %>
14
+ <% end %>
15
+ <% end %>
16
+
17
+ <%= ui_section(title: "Create a diagnostic") do %>
18
+ <% if @diagnostic&.errors&.any? %>
19
+ <%= ui_alert(message: @diagnostic.errors.full_messages.to_sentence, type: :error) %>
20
+ <% end %>
21
+
22
+ <%= ui_form(action: alembic.manage_flows_path, method: :post) do %>
23
+ <label class="block text-sm font-medium mb-1">Slug</label>
24
+ <%= ui_input(name: "flow[slug]", placeholder: "e.g. business-scorecard") %>
25
+
26
+ <label class="block text-sm font-medium mb-1 mt-4">Kind</label>
27
+ <%= ui_select(name: "flow[kind]", options: [ [ "Scored", "scored" ], [ "Guide", "guide" ] ]) %>
28
+
29
+ <%= ui_button(label: "Create", type: :submit, variant: :secondary) %>
30
+ <% end %>
31
+ <% end %>
32
+ <% end %>
@@ -0,0 +1,26 @@
1
+ <% content_for :head do %>
2
+ <%= javascript_include_tag "keystone_ui/react", defer: true %>
3
+ <%= javascript_include_tag "alembic/canvas", defer: true %>
4
+ <% end %>
5
+
6
+ <div class="flex h-screen flex-col overflow-hidden">
7
+ <header class="flex shrink-0 items-center justify-between gap-4 border-b border-gray-200 px-6 py-3">
8
+ <div>
9
+ <h1 class="text-lg font-semibold text-gray-900" data-flow-heading><%= @diagnostic.title.presence || @diagnostic.slug %></h1>
10
+ <p class="text-sm text-gray-500"><%= @diagnostic.slug %></p>
11
+ </div>
12
+ <div class="flex items-center gap-2">
13
+ <button type="button" data-open-panel
14
+ onclick="document.dispatchEvent(new CustomEvent('alembic:open-flow'))"
15
+ class="inline-flex items-center justify-center font-semibold rounded-lg border-0 cursor-pointer bg-accent-600 text-white hover:bg-accent-700 text-base px-4 py-2">This flow</button>
16
+
17
+ <%= ui_button(label: "Try it", href: alembic.manage_flow_preview_path(@diagnostic), variant: :secondary) %>
18
+
19
+ <%= ui_button(label: "All diagnostics", href: alembic.manage_flows_path, variant: :secondary) %>
20
+ </div>
21
+ </header>
22
+
23
+ <%= react_ui("alembic/flow-editor", { base: alembic.manage_flow_canvas_path(@diagnostic), token: form_authenticity_token, initial: @canvas },
24
+ class: "min-h-0 flex-1",
25
+ data: { flow_canvas: true }) %>
26
+ </div>
@@ -0,0 +1 @@
1
+ <%= KsBlocks.registry.block_types(kind: :pages).find { |type| type.key.to_s == block["type"] }&.name %>
@@ -0,0 +1,20 @@
1
+ <%= ui_page(max_width: :xl) do %>
2
+ <%= ui_page_header(title: "Pages", subtitle: "Build and edit your pages") %>
3
+
4
+ <%= ui_data_table(
5
+ items: @pages,
6
+ columns: [ { name: "Name" } ],
7
+ empty_message: "No pages yet."
8
+ ) do |table| %>
9
+ <% table.link(:name) { |page| alembic.manage_page_path(page) } %>
10
+ <% end %>
11
+
12
+ <%= ui_section(title: "Create a page") do %>
13
+ <%= ui_form(action: alembic.manage_pages_path, method: :post) do %>
14
+ <label class="block text-sm font-medium mb-1">Name</label>
15
+ <%= ui_input(name: "page[name]", placeholder: "e.g. Welcome") %>
16
+
17
+ <%= ui_button(label: "Create", type: :submit, variant: :secondary) %>
18
+ <% end %>
19
+ <% end %>
20
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <% content_for :head do %>
2
+ <%= stylesheet_link_tag "alembic/page_builder" %>
3
+ <%= javascript_include_tag "keystone_ui/react", defer: true %>
4
+ <%= javascript_include_tag "alembic/page_builder", defer: true %>
5
+ <% end %>
6
+
7
+ <%= react_ui("alembic/page-builder", @payload.merge(token: form_authenticity_token)) %>
8
+
9
+ <%= block_contents(@page.blocks) do |block| %>
10
+ <%= render "alembic/manage/pages/block_content", block: block %>
11
+ <% end %>
@@ -0,0 +1,41 @@
1
+ <%= ui_page(max_width: :lg) do %>
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" %>
4
+ </p>
5
+
6
+ <%= ui_page_header(title: "History", subtitle: @diagnostic.title.presence || @diagnostic.slug) %>
7
+
8
+ <%= ui_section do %>
9
+ <ul class="divide-y divide-gray-100 border-y border-gray-100">
10
+ <% @versions.each do |version| %>
11
+ <li data-version="<%= version.number %>" class="py-4">
12
+ <div class="flex items-baseline justify-between gap-4">
13
+ <span class="font-medium text-gray-900">Version <%= version.number %></span>
14
+ <span class="text-sm text-gray-500"><%= version.created_at.to_fs(:long) %></span>
15
+ </div>
16
+
17
+ <% if version == @diagnostic.live_version %>
18
+ <p data-live class="mt-1 text-sm font-medium text-green-700">Visitors run this version</p>
19
+ <% end %>
20
+
21
+ <% if version.changes.any? %>
22
+ <ul class="mt-2 space-y-0.5">
23
+ <% version.changes.each do |change| %>
24
+ <li data-captured class="text-sm text-gray-600"><%= Alembic::Flow::Change.phrase(change) %></li>
25
+ <% end %>
26
+ </ul>
27
+ <% else %>
28
+ <p class="mt-2 text-sm text-gray-500">Nothing was recorded against this version.</p>
29
+ <% end %>
30
+
31
+ <% unless version == @versions.first %>
32
+ <%= button_to "Return the flow to this version",
33
+ alembic.return_manage_flow_version_path(@diagnostic, version),
34
+ form: { data: { turbo: false } }, data: { return: true },
35
+ class: "mt-3 inline-flex items-center rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm text-gray-900 hover:bg-gray-50 cursor-pointer" %>
36
+ <% end %>
37
+ </li>
38
+ <% end %>
39
+ </ul>
40
+ <% end %>
41
+ <% end %>
@@ -0,0 +1,17 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Alembic</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= yield :head %>
9
+
10
+ <%= stylesheet_link_tag "alembic/application", media: "all" %>
11
+ </head>
12
+ <body>
13
+
14
+ <%= yield %>
15
+
16
+ </body>
17
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,42 @@
1
+ Alembic::Engine.routes.draw do
2
+ namespace :manage do
3
+ resources :pages, only: [ :index, :create, :show ] do
4
+ get "layout", to: "page_blocks#layout", as: :layout
5
+ post "blocks", to: "page_blocks#add_block", as: :blocks
6
+ patch "blocks", to: "page_blocks#place_blocks"
7
+ patch "blocks/:block_id", to: "page_blocks#fill_block"
8
+ delete "blocks/:block_id", to: "page_blocks#remove_block", as: :block
9
+ end
10
+
11
+ resources :flows, only: [ :index, :create, :show, :edit, :update, :destroy ] do
12
+ resource :definition, only: [ :edit, :update ]
13
+ resource :preview, only: :show, controller: "previews" do
14
+ get :step
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
+ end
34
+ end
35
+
36
+ post ":slug/runs", to: "flows#start", as: :flow_runs
37
+ get "runs/:id", to: "flows#step", as: :run
38
+ patch "runs/:id", to: "flows#update"
39
+
40
+ get ":slug", to: "flows#show", as: :flow
41
+ get ":slug/step", to: "flows#step", as: :flow_step
42
+ end
@@ -0,0 +1,14 @@
1
+ class CreateAlembicDiagnostics < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_diagnostics do |t|
4
+ t.string :slug
5
+ t.string :title
6
+ t.text :summary
7
+ t.string :kind
8
+ t.string :status
9
+
10
+ t.timestamps
11
+ end
12
+ add_index :alembic_diagnostics, :slug, unique: true
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ class CreateAlembicQuestions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_questions do |t|
4
+ t.references :diagnostic, null: false, foreign_key: { to_table: :alembic_diagnostics }
5
+ t.integer :position
6
+ t.string :key
7
+ t.text :text
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ class CreateAlembicOptions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_options do |t|
4
+ t.references :question, null: false, foreign_key: { to_table: :alembic_questions }
5
+ t.integer :position
6
+ t.string :value
7
+ t.string :label
8
+ t.text :hint
9
+ t.integer :weight
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ class CreateAlembicConditions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_conditions do |t|
4
+ t.references :question, null: false, foreign_key: { to_table: :alembic_questions }
5
+ t.string :depends_on
6
+ t.json :values
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ class CreateAlembicBands < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_bands do |t|
4
+ t.references :diagnostic, null: false, foreign_key: { to_table: :alembic_diagnostics }
5
+ t.integer :ceiling
6
+ t.string :name
7
+ t.text :description
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ class CreateAlembicConditionOptions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_condition_options do |t|
4
+ t.references :condition, null: false, foreign_key: { to_table: :alembic_conditions }
5
+ t.references :option, null: false, foreign_key: { to_table: :alembic_options }
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ class ReshapeConditionsForPlacement < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_reference :alembic_conditions, :subject, polymorphic: true, null: false
4
+ add_reference :alembic_conditions, :tested_question, null: false,
5
+ foreign_key: { to_table: :alembic_questions }
6
+
7
+ remove_reference :alembic_conditions, :question, foreign_key: { to_table: :alembic_questions }
8
+ remove_column :alembic_conditions, :depends_on, :string
9
+ remove_column :alembic_conditions, :values, :json
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ class CreateAlembicResults < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_results do |t|
4
+ t.references :diagnostic, null: false, foreign_key: { to_table: :alembic_diagnostics }
5
+ t.string :slot
6
+ t.string :key
7
+ t.string :title
8
+ t.text :body
9
+ t.integer :position
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ class CreateAlembicRules < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_rules do |t|
4
+ t.references :diagnostic, null: false, foreign_key: { to_table: :alembic_diagnostics }
5
+ t.integer :position
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class CreateAlembicRuleResults < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_rule_results do |t|
4
+ t.references :rule, null: false, foreign_key: { to_table: :alembic_rules }
5
+ t.references :result, null: false, foreign_key: { to_table: :alembic_results }
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class AddDefinitionToAlembicDiagnostics < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :alembic_diagnostics, :definition, :json
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class AddGuideCopyToAlembicDiagnostics < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :alembic_diagnostics, :kicker, :string
4
+ add_column :alembic_diagnostics, :headline, :string
5
+ add_column :alembic_diagnostics, :blurb, :text
6
+ add_column :alembic_diagnostics, :start_label, :string
7
+ add_column :alembic_diagnostics, :resolver_key, :string
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ class CreateAlembicNodes < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_nodes do |t|
4
+ t.references :diagnostic, null: false, foreign_key: { to_table: :alembic_diagnostics }
5
+ t.string :kind
6
+ t.string :key
7
+ t.integer :position
8
+ t.string :name
9
+ t.text :tagline
10
+ t.text :complexity
11
+ t.text :setup
12
+ t.text :maintenance
13
+ t.text :captures
14
+ t.text :why
15
+ t.text :pains
16
+ t.text :avoid
17
+ t.text :avoid_pain
18
+
19
+ t.timestamps
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ class CreateAlembicBuildSteps < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_build_steps do |t|
4
+ t.references :node, null: false, foreign_key: { to_table: :alembic_nodes }
5
+ t.integer :position
6
+ t.string :title
7
+ t.text :code
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ class CreateAlembicWarnings < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_warnings do |t|
4
+ t.references :diagnostic, null: false, foreign_key: { to_table: :alembic_diagnostics }
5
+ t.string :key
6
+ t.text :text
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ class CreateAlembicDomains < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_domains do |t|
4
+ t.references :diagnostic, null: false, foreign_key: { to_table: :alembic_diagnostics }
5
+ t.string :key
6
+ t.string :name
7
+ t.text :gap_meaning
8
+ t.text :gap_cost
9
+ t.integer :position
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ class AddDomainToAlembicQuestions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_reference :alembic_questions, :domain, foreign_key: { to_table: :alembic_domains }
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ class CreateAlembicDefinitionVersions < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :alembic_definition_versions do |t|
4
+ t.references :diagnostic, null: false, foreign_key: { to_table: :alembic_diagnostics }
5
+ t.integer :number, null: false
6
+ t.json :definition
7
+
8
+ t.datetime :created_at, null: false
9
+ end
10
+
11
+ add_index :alembic_definition_versions, [ :diagnostic_id, :number ], unique: true,
12
+ name: "index_alembic_definition_versions_on_diagnostic_and_number"
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ class BackfillAlembicDefinitionVersions < ActiveRecord::Migration[8.1]
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.where.not(definition: nil).find_each do |diagnostic|
12
+ next if DefinitionVersion.exists?(diagnostic_id: diagnostic.id)
13
+
14
+ DefinitionVersion.create!(diagnostic_id: diagnostic.id, number: 1, definition: diagnostic.definition)
15
+ end
16
+ end
17
+ end