easy_flow 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.
- checksums.yaml +4 -4
- data/app/assets/builds/easy_flow/canvas.js +1 -0
- data/app/controllers/concerns/easy_flow/manage/authenticates_admin.rb +19 -0
- data/app/controllers/concerns/easy_flow/manage/draws_canvas.rb +34 -0
- data/app/controllers/easy_flow/application_controller.rb +32 -0
- data/app/controllers/easy_flow/flows_controller.rb +118 -0
- data/app/controllers/easy_flow/manage/base_controller.rb +14 -0
- data/app/controllers/easy_flow/manage/canvas_controller.rb +183 -0
- data/app/controllers/easy_flow/manage/definitions_controller.rb +15 -0
- data/app/controllers/easy_flow/manage/flows_controller.rb +55 -0
- data/app/controllers/easy_flow/manage/previews_controller.rb +38 -0
- data/app/controllers/easy_flow/manage/versions_controller.rb +22 -0
- data/app/javascript/easy_flow/canvas/Canvas.jsx +197 -0
- data/app/javascript/easy_flow/canvas/Connector.jsx +30 -0
- data/app/javascript/easy_flow/canvas/ConnectorLayer.jsx +31 -0
- data/app/javascript/easy_flow/canvas/Control.jsx +42 -0
- data/app/javascript/easy_flow/canvas/Inspector.jsx +44 -0
- data/app/javascript/easy_flow/canvas/Panel.jsx +61 -0
- data/app/javascript/easy_flow/canvas/Placeholder.jsx +24 -0
- data/app/javascript/easy_flow/canvas/Port.jsx +16 -0
- data/app/javascript/easy_flow/canvas/Records.jsx +34 -0
- data/app/javascript/easy_flow/canvas/StepCard.jsx +57 -0
- data/app/javascript/easy_flow/canvas/Toolbar.jsx +19 -0
- data/app/javascript/easy_flow/canvas/TypePicker.jsx +21 -0
- data/app/javascript/easy_flow/canvas/changes.js +5 -0
- data/app/javascript/easy_flow/canvas/choices.js +4 -0
- data/app/javascript/easy_flow/canvas/flow.js +7 -0
- data/app/javascript/easy_flow/canvas/flowSender.js +22 -0
- data/app/javascript/easy_flow/canvas/ids.js +7 -0
- data/app/javascript/easy_flow/canvas/rows.js +2 -0
- data/app/javascript/easy_flow/canvas/styles.js +3 -0
- data/app/javascript/easy_flow/canvas/useConnectors.js +75 -0
- data/app/javascript/easy_flow/canvas/useFlow.js +17 -0
- data/app/javascript/easy_flow/canvas.jsx +13 -0
- data/app/views/easy_flow/flows/complete.html.erb +22 -0
- data/app/views/easy_flow/flows/show.html.erb +17 -0
- data/app/views/easy_flow/flows/step.html.erb +27 -0
- data/app/views/easy_flow/manage/definitions/edit.html.erb +13 -0
- data/app/views/easy_flow/manage/flows/edit.html.erb +26 -0
- data/app/views/easy_flow/manage/flows/index.html.erb +32 -0
- data/app/views/easy_flow/manage/flows/show.html.erb +26 -0
- data/app/views/easy_flow/manage/versions/index.html.erb +41 -0
- data/app/views/easy_flow/steps/_choosing.html.erb +9 -0
- data/app/views/layouts/easy_flow/application.html.erb +15 -0
- data/config/routes.rb +32 -0
- data/lib/easy_flow/engine.rb +11 -0
- data/lib/easy_flow/version.rb +1 -1
- data/lib/easy_flow.rb +15 -0
- metadata +58 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { useLayoutEffect, useState } from "react"
|
|
2
|
+
|
|
3
|
+
export const anchor = (box, side, frame, surface) => {
|
|
4
|
+
const left = box.left - frame.left + surface.scrollLeft
|
|
5
|
+
const top = box.top - frame.top + surface.scrollTop
|
|
6
|
+
|
|
7
|
+
if (side === "left") return { x: left, y: top + box.height / 2 }
|
|
8
|
+
if (side === "right") return { x: left + box.width, y: top + box.height / 2 }
|
|
9
|
+
if (side === "top") return { x: left + box.width / 2, y: top }
|
|
10
|
+
return { x: left + box.width / 2, y: top + box.height }
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const shy = (point, side) => {
|
|
14
|
+
if (side === "top") return { x: point.x, y: point.y - 3 }
|
|
15
|
+
if (side === "left") return { x: point.x - 3, y: point.y }
|
|
16
|
+
if (side === "right") return { x: point.x + 3, y: point.y }
|
|
17
|
+
return { x: point.x, y: point.y + 3 }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const drawn = (edge, a, b, lane, detour) => {
|
|
21
|
+
const path = {
|
|
22
|
+
straight: `M ${a.x} ${a.y} L ${b.x} ${b.y}`,
|
|
23
|
+
turn: `M ${a.x} ${a.y} H ${b.x} V ${b.y}`,
|
|
24
|
+
lane: `M ${a.x} ${a.y} V ${lane} H ${b.x} V ${b.y}`,
|
|
25
|
+
detour: `M ${a.x} ${a.y} H ${detour} V ${b.y} H ${b.x}`
|
|
26
|
+
}[edge.route]
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
...edge, path,
|
|
30
|
+
midX: edge.route === "detour" ? detour : (a.x + b.x) / 2,
|
|
31
|
+
midY: edge.route === "lane" ? lane : (a.y + b.y) / 2
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const useConnectors = (flow, surface, cards, watching) => {
|
|
36
|
+
const [ links, setLinks ] = useState([])
|
|
37
|
+
const [ extent, setExtent ] = useState({ width: "100%", height: "100%" })
|
|
38
|
+
|
|
39
|
+
useLayoutEffect(() => {
|
|
40
|
+
const measure = () => {
|
|
41
|
+
const frame = surface.current?.getBoundingClientRect()
|
|
42
|
+
if (!frame) return
|
|
43
|
+
|
|
44
|
+
setExtent({ width: surface.current.scrollWidth, height: surface.current.scrollHeight })
|
|
45
|
+
|
|
46
|
+
setLinks(flow.edges.flatMap((edge) => {
|
|
47
|
+
const fromBox = cards.current[edge.source]?.getBoundingClientRect()
|
|
48
|
+
const toBox = cards.current[edge.target]?.getBoundingClientRect()
|
|
49
|
+
if (!fromBox || !toBox) return []
|
|
50
|
+
|
|
51
|
+
const a = anchor(fromBox, edge.leaves, frame, surface.current)
|
|
52
|
+
const b = shy(anchor(toBox, edge.enters, frame, surface.current), edge.enters)
|
|
53
|
+
const lane = (anchor(fromBox, "bottom", frame, surface.current).y + anchor(toBox, "top", frame, surface.current).y) / 2
|
|
54
|
+
const detour = edge.leaves === "left" ? Math.min(a.x, b.x) - 28 : Math.max(a.x, b.x) + 28
|
|
55
|
+
|
|
56
|
+
return [ drawn(edge, a, b, lane, detour) ]
|
|
57
|
+
}))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
measure()
|
|
61
|
+
window.addEventListener("resize", measure)
|
|
62
|
+
|
|
63
|
+
const watcher = new ResizeObserver(measure)
|
|
64
|
+
if (surface.current) watcher.observe(surface.current)
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
window.removeEventListener("resize", measure)
|
|
68
|
+
watcher.disconnect()
|
|
69
|
+
}
|
|
70
|
+
}, watching)
|
|
71
|
+
|
|
72
|
+
return { links, extent }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export default useConnectors
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useMemo, useState } from "react"
|
|
2
|
+
import { createFlowSender } from "./flowSender"
|
|
3
|
+
|
|
4
|
+
const useFlow = (base, token, initial) => {
|
|
5
|
+
const [ flow, setFlow ] = useState(initial)
|
|
6
|
+
const [ error, setError ] = useState(null)
|
|
7
|
+
const [ notice, setNotice ] = useState(null)
|
|
8
|
+
|
|
9
|
+
const send = useMemo(
|
|
10
|
+
() => createFlowSender({ base, token, fetch: (...request) => window.fetch(...request), onFlow: setFlow, onError: setError, onNotice: setNotice }),
|
|
11
|
+
[ base, token ]
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
return { flow, error, notice, send }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default useFlow
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createRoot } from "react-dom/client"
|
|
2
|
+
import { register } from "keystone_ui-react/src/registry.js"
|
|
3
|
+
import { startMounting } from "keystone_ui-react/src/mounting.js"
|
|
4
|
+
import Canvas from "./canvas/Canvas"
|
|
5
|
+
|
|
6
|
+
register("easy_flow/flow-editor", Canvas)
|
|
7
|
+
|
|
8
|
+
document.addEventListener("easy_flow:flow-named", (event) => {
|
|
9
|
+
const heading = document.querySelector("[data-flow-heading]")
|
|
10
|
+
if (heading) heading.textContent = event.detail
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
startMounting(document, createRoot)
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
<%= ui_section do %>
|
|
13
|
+
<ul class="divide-y divide-gray-100 border-y border-gray-100">
|
|
14
|
+
<% @answered.each do |step, value| %>
|
|
15
|
+
<li data-answer="<%= step %>" class="flex items-baseline justify-between gap-6 py-3">
|
|
16
|
+
<span class="text-gray-600"><%= @guide.question_text(step) %></span>
|
|
17
|
+
<span class="shrink-0 font-medium text-gray-900"><%= @guide.choice_label(step, value) %></span>
|
|
18
|
+
</li>
|
|
19
|
+
<% end %>
|
|
20
|
+
</ul>
|
|
21
|
+
<% end %>
|
|
22
|
+
<% end %>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<% content_for(:title, @flow.title.presence || @flow.slug) %>
|
|
2
|
+
|
|
3
|
+
<%= ui_page(max_width: :md) do %>
|
|
4
|
+
<%= ui_page_header(title: @flow.title.presence || @flow.slug) %>
|
|
5
|
+
|
|
6
|
+
<%= ui_section do %>
|
|
7
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
8
|
+
<% if @flow.each_step? && @flow.definition.present? && !previewing? %>
|
|
9
|
+
<%= ui_form(action: easy_flow.flow_runs_path(@flow.slug), method: :post) do %>
|
|
10
|
+
<%= ui_button(label: @flow.start_label.presence || "Start", type: :submit) %>
|
|
11
|
+
<% end %>
|
|
12
|
+
<% else %>
|
|
13
|
+
<%= ui_button(label: @flow.start_label.presence || "Start", href: flow_step_path(@flow.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", easy_flow.manage_flow_path(@flow), class: "text-sm text-accent-600 hover:text-accent-700" %>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<%= ui_form_page(title: "Definition", back_url: easy_flow.manage_flow_path(@flow), subtitle: @flow.slug) %>
|
|
7
|
+
|
|
8
|
+
<%= ui_form(action: easy_flow.manage_flow_definition_path(@flow), method: :patch) do %>
|
|
9
|
+
<%= ui_textarea(name: "definition", value: JSON.pretty_generate(@flow.document), rows: 30) %>
|
|
10
|
+
|
|
11
|
+
<%= ui_button(label: "Save", type: :submit) %>
|
|
12
|
+
<% end %>
|
|
13
|
+
<% end %>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<%= ui_page(max_width: :lg) do %>
|
|
2
|
+
<p class="mb-4">
|
|
3
|
+
<%= link_to "← Back to the flow", easy_flow.manage_flow_path(@flow), class: "text-sm text-accent-600 hover:text-accent-700" %>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<%= ui_form_page(title: "Edit details", back_url: easy_flow.manage_flow_path(@flow)) %>
|
|
7
|
+
|
|
8
|
+
<%= ui_form(action: easy_flow.manage_flow_path(@flow), 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: @flow.title) %>
|
|
12
|
+
|
|
13
|
+
<label class="block text-sm font-medium mb-1 mt-4">Start label</label>
|
|
14
|
+
<%= ui_input(name: "flow[start_label]", value: @flow.start_label) %>
|
|
15
|
+
<% end %>
|
|
16
|
+
|
|
17
|
+
<%= ui_section(title: "What this keeps of a run") do %>
|
|
18
|
+
<%= ui_select(name: "flow[persists]", selected: @flow.persists,
|
|
19
|
+
options: [ [ "Nothing", "unsaved" ],
|
|
20
|
+
[ "Every answer as it is given", "each_step" ],
|
|
21
|
+
[ "The whole run once it finishes", "on_finish" ] ]) %>
|
|
22
|
+
<% end %>
|
|
23
|
+
|
|
24
|
+
<%= ui_button(label: "Save", type: :submit) %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<% 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: @flows,
|
|
6
|
+
columns: [ { title: "Title" }, { slug: "Slug" }, { kind: "Kind" }, { status: "Status" } ],
|
|
7
|
+
empty_message: "No flows yet."
|
|
8
|
+
) do |table| %>
|
|
9
|
+
<% table.link(:title) { |flow| easy_flow.manage_flow_path(flow) } %>
|
|
10
|
+
<% table.actions do |flow| %>
|
|
11
|
+
<%= ui_form(action: easy_flow.manage_flow_path(flow), 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 flow") do %>
|
|
18
|
+
<% if @flow&.errors&.any? %>
|
|
19
|
+
<%= ui_alert(message: @flow.errors.full_messages.to_sentence, type: :error) %>
|
|
20
|
+
<% end %>
|
|
21
|
+
|
|
22
|
+
<%= ui_form(action: easy_flow.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 "easy_flow/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><%= @flow.title.presence || @flow.slug %></h1>
|
|
10
|
+
<p class="text-sm text-gray-500"><%= @flow.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('easy_flow: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: easy_flow.manage_flow_preview_path(@flow), variant: :secondary) %>
|
|
18
|
+
|
|
19
|
+
<%= ui_button(label: "All flows", href: easy_flow.manage_flows_path, variant: :secondary) %>
|
|
20
|
+
</div>
|
|
21
|
+
</header>
|
|
22
|
+
|
|
23
|
+
<%= react_ui("easy_flow/flow-editor", { base: easy_flow.manage_flow_canvas_path(@flow), token: form_authenticity_token, initial: @canvas },
|
|
24
|
+
class: "min-h-0 flex-1",
|
|
25
|
+
data: { flow_canvas: true }) %>
|
|
26
|
+
</div>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<%= ui_page(max_width: :lg) do %>
|
|
2
|
+
<p class="mb-4">
|
|
3
|
+
<%= link_to "← Back to the flow", easy_flow.manage_flow_path(@flow), class: "text-sm text-accent-600 hover:text-accent-700" %>
|
|
4
|
+
</p>
|
|
5
|
+
|
|
6
|
+
<%= ui_page_header(title: "History", subtitle: @flow.title.presence || @flow.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 == @flow.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"><%= EasyFlow::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
|
+
easy_flow.return_manage_flow_version_path(@flow, 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,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>
|
data/config/routes.rb
CHANGED
|
@@ -1,2 +1,34 @@
|
|
|
1
1
|
EasyFlow::Engine.routes.draw do
|
|
2
|
+
namespace :manage do
|
|
3
|
+
resources :flows, only: [ :index, :create, :show, :edit, :update, :destroy ] do
|
|
4
|
+
resource :definition, only: [ :edit, :update ]
|
|
5
|
+
resource :preview, only: :show, controller: "previews" do
|
|
6
|
+
get :step
|
|
7
|
+
end
|
|
8
|
+
resources :versions, only: :index do
|
|
9
|
+
post :return, on: :member
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
resource :canvas, only: :show, controller: "canvas" do
|
|
13
|
+
post "steps", action: :add_step
|
|
14
|
+
patch "steps/:step", action: :configure_step
|
|
15
|
+
delete "steps/:step", action: :remove_step
|
|
16
|
+
patch "steps/:step/move", action: :move_step
|
|
17
|
+
post "versions", action: :create
|
|
18
|
+
post "publish", action: :publish
|
|
19
|
+
patch "details", action: :details
|
|
20
|
+
post "undo", action: :undo
|
|
21
|
+
post "redo", action: :redo
|
|
22
|
+
post "edges", action: :connect
|
|
23
|
+
delete "edges", action: :disconnect
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
post ":slug/runs", to: "flows#start", as: :flow_runs
|
|
29
|
+
get "runs/:id", to: "flows#step", as: :run
|
|
30
|
+
patch "runs/:id", to: "flows#update"
|
|
31
|
+
|
|
32
|
+
get ":slug", to: "flows#show", as: :flow
|
|
33
|
+
get ":slug/step", to: "flows#step", as: :flow_step
|
|
2
34
|
end
|
data/lib/easy_flow/engine.rb
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
+
require "keystone_ui-react"
|
|
2
|
+
|
|
1
3
|
module EasyFlow
|
|
2
4
|
class Engine < ::Rails::Engine
|
|
3
5
|
isolate_namespace EasyFlow
|
|
4
6
|
|
|
7
|
+
initializer "easy_flow.tailwind" do
|
|
8
|
+
next unless Gem.loaded_specs.key?("keystone_ui")
|
|
9
|
+
|
|
10
|
+
require "keystone_ui"
|
|
11
|
+
KeystoneUi.configuration.tailwind_sources << root.join("app/views/**/*.erb").to_s
|
|
12
|
+
KeystoneUi.configuration.tailwind_sources << root.join("app/javascript/**/*.{js,jsx}").to_s
|
|
13
|
+
KeystoneUi.configuration.tailwind_sources << root.join("app/assets/builds/easy_flow/*.js").to_s
|
|
14
|
+
end
|
|
15
|
+
|
|
5
16
|
initializer "easy_flow.step_types" do |app|
|
|
6
17
|
app.config.to_prepare do
|
|
7
18
|
EasyFlow::Start.register
|
data/lib/easy_flow/version.rb
CHANGED
data/lib/easy_flow.rb
CHANGED
|
@@ -21,6 +21,21 @@ module EasyFlow
|
|
|
21
21
|
DRAWING = "easy_flow/steps/choosing".freeze
|
|
22
22
|
|
|
23
23
|
class << self
|
|
24
|
+
attr_writer :layout, :base_controller, :admin_layout
|
|
25
|
+
attr_accessor :visitor_authorization_method, :refusal_method, :admin_authentication_method
|
|
26
|
+
|
|
27
|
+
def layout
|
|
28
|
+
@layout || "easy_flow/application"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def base_controller
|
|
32
|
+
@base_controller || "ActionController::Base"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def admin_layout
|
|
36
|
+
@admin_layout || "application"
|
|
37
|
+
end
|
|
38
|
+
|
|
24
39
|
def draws_with(template)
|
|
25
40
|
@drawing = template
|
|
26
41
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: easy_flow
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- tylercschneider
|
|
@@ -23,6 +23,20 @@ dependencies:
|
|
|
23
23
|
- - ">="
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: 8.1.3
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: keystone_ui-react
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.1.2
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.1.2
|
|
26
40
|
description: EasyFlow stores flows as versioned documents of steps and the connections
|
|
27
41
|
between them, and runs a visitor through the live version one step at a time. Apps
|
|
28
42
|
register their own step types.
|
|
@@ -35,6 +49,39 @@ files:
|
|
|
35
49
|
- MIT-LICENSE
|
|
36
50
|
- README.md
|
|
37
51
|
- Rakefile
|
|
52
|
+
- app/assets/builds/easy_flow/canvas.js
|
|
53
|
+
- app/controllers/concerns/easy_flow/manage/authenticates_admin.rb
|
|
54
|
+
- app/controllers/concerns/easy_flow/manage/draws_canvas.rb
|
|
55
|
+
- app/controllers/easy_flow/application_controller.rb
|
|
56
|
+
- app/controllers/easy_flow/flows_controller.rb
|
|
57
|
+
- app/controllers/easy_flow/manage/base_controller.rb
|
|
58
|
+
- app/controllers/easy_flow/manage/canvas_controller.rb
|
|
59
|
+
- app/controllers/easy_flow/manage/definitions_controller.rb
|
|
60
|
+
- app/controllers/easy_flow/manage/flows_controller.rb
|
|
61
|
+
- app/controllers/easy_flow/manage/previews_controller.rb
|
|
62
|
+
- app/controllers/easy_flow/manage/versions_controller.rb
|
|
63
|
+
- app/javascript/easy_flow/canvas.jsx
|
|
64
|
+
- app/javascript/easy_flow/canvas/Canvas.jsx
|
|
65
|
+
- app/javascript/easy_flow/canvas/Connector.jsx
|
|
66
|
+
- app/javascript/easy_flow/canvas/ConnectorLayer.jsx
|
|
67
|
+
- app/javascript/easy_flow/canvas/Control.jsx
|
|
68
|
+
- app/javascript/easy_flow/canvas/Inspector.jsx
|
|
69
|
+
- app/javascript/easy_flow/canvas/Panel.jsx
|
|
70
|
+
- app/javascript/easy_flow/canvas/Placeholder.jsx
|
|
71
|
+
- app/javascript/easy_flow/canvas/Port.jsx
|
|
72
|
+
- app/javascript/easy_flow/canvas/Records.jsx
|
|
73
|
+
- app/javascript/easy_flow/canvas/StepCard.jsx
|
|
74
|
+
- app/javascript/easy_flow/canvas/Toolbar.jsx
|
|
75
|
+
- app/javascript/easy_flow/canvas/TypePicker.jsx
|
|
76
|
+
- app/javascript/easy_flow/canvas/changes.js
|
|
77
|
+
- app/javascript/easy_flow/canvas/choices.js
|
|
78
|
+
- app/javascript/easy_flow/canvas/flow.js
|
|
79
|
+
- app/javascript/easy_flow/canvas/flowSender.js
|
|
80
|
+
- app/javascript/easy_flow/canvas/ids.js
|
|
81
|
+
- app/javascript/easy_flow/canvas/rows.js
|
|
82
|
+
- app/javascript/easy_flow/canvas/styles.js
|
|
83
|
+
- app/javascript/easy_flow/canvas/useConnectors.js
|
|
84
|
+
- app/javascript/easy_flow/canvas/useFlow.js
|
|
38
85
|
- app/models/easy_flow/admission.rb
|
|
39
86
|
- app/models/easy_flow/application_record.rb
|
|
40
87
|
- app/models/easy_flow/asked.rb
|
|
@@ -70,6 +117,16 @@ files:
|
|
|
70
117
|
- app/models/easy_flow/validator.rb
|
|
71
118
|
- app/models/easy_flow/version.rb
|
|
72
119
|
- app/models/easy_flow/violation.rb
|
|
120
|
+
- app/views/easy_flow/flows/complete.html.erb
|
|
121
|
+
- app/views/easy_flow/flows/show.html.erb
|
|
122
|
+
- app/views/easy_flow/flows/step.html.erb
|
|
123
|
+
- app/views/easy_flow/manage/definitions/edit.html.erb
|
|
124
|
+
- app/views/easy_flow/manage/flows/edit.html.erb
|
|
125
|
+
- app/views/easy_flow/manage/flows/index.html.erb
|
|
126
|
+
- app/views/easy_flow/manage/flows/show.html.erb
|
|
127
|
+
- app/views/easy_flow/manage/versions/index.html.erb
|
|
128
|
+
- app/views/easy_flow/steps/_choosing.html.erb
|
|
129
|
+
- app/views/layouts/easy_flow/application.html.erb
|
|
73
130
|
- config/routes.rb
|
|
74
131
|
- db/migrate/20260924120000_create_easy_flow_definitions.rb
|
|
75
132
|
- db/migrate/20260924120100_create_easy_flow_versions.rb
|