workflow charts

This commit is contained in:
Martin McKeaveney 2020-05-22 16:32:23 +01:00
parent e659e69e86
commit cacee947bf
14 changed files with 146 additions and 60 deletions

View File

@ -18,10 +18,12 @@ const post = apiCall("POST")
const get = apiCall("GET") const get = apiCall("GET")
const patch = apiCall("PATCH") const patch = apiCall("PATCH")
const del = apiCall("DELETE") const del = apiCall("DELETE")
const put = apiCall("PUT")
export default { export default {
post, post,
get, get,
patch, patch,
delete: del, delete: del,
put
} }

View File

@ -1,14 +1,7 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
import api from "../api" import api from "../api"
export const getWorkflowStore = () => { const workflowActions = store => ({
const INITIAL_WORKFLOW_STATE = {
workflows: []
}
const store = writable(INITIAL_WORKFLOW_STATE)
store.actions = {
fetch: async instanceId => { fetch: async instanceId => {
const WORKFLOWS_URL = `/api/${instanceId}/workflows`; const WORKFLOWS_URL = `/api/${instanceId}/workflows`;
const workflowResponse = await api.get(WORKFLOWS_URL); const workflowResponse = await api.get(WORKFLOWS_URL);
@ -25,10 +18,37 @@ export const getWorkflowStore = () => {
const json = await response.json(); const json = await response.json();
store.update(state => { store.update(state => {
state.workflows = state.workflows.concat(json.workflow) state.workflows = state.workflows.concat(json.workflow)
state.selectedWorkflowId = json.workflow._id
return state return state
}) })
}, },
update: async ({ instanceId, workflow }) => {
const UPDATE_WORKFLOW_URL = `/api/${instanceId}/workflows`;
const response = await api.put(UPDATE_WORKFLOW_URL, workflow)
const json = await response.json();
store.update(state => {
const existingIdx = state.workflows.findIndex(existing => existing._id === workflow._id);
state.workflows.splice(existingIdx, 1, json.workflow);
state.workflows = state.workflows
return state
})
},
select: workflow => {
store.update(state => {
state.selectedWorkflowId = workflow._id
return state;
})
} }
});
export const getWorkflowStore = () => {
const INITIAL_WORKFLOW_STATE = {
workflows: []
}
const store = writable(INITIAL_WORKFLOW_STATE)
store.actions = workflowActions(store);
return store return store
} }

View File

@ -293,6 +293,7 @@ body, html {
.grabme, .blockico { .grabme, .blockico {
display: inline-block; display: inline-block;
} }
.grabme { .grabme {
margin-top: 10px; margin-top: 10px;
margin-left: 10px; margin-left: 10px;
@ -322,6 +323,12 @@ body, html {
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
} }
.blockico i {
font-size: 24px;
color: var(--dark-grey);
}
.blockico span { .blockico span {
height: 100%; height: 100%;
width: 0px; width: 0px;

View File

@ -42,19 +42,18 @@
</div> </div>
<div id="blocklist"> <div id="blocklist">
{#each definitions as blockDefinition} {#each definitions as blockDefinition}
<div class="blockelem create-flowy noselect"> <div
class="blockelem create-flowy noselect"
data-name={blockDefinition.name}
>
<input <input
type="hidden" type="hidden"
name="blockelemtype" name="blockelemtype"
class="blockelemtype" class="blockelemtype"
value="1" /> value="1" />
<div class="grabme">
<!-- <img src="assets/grabme.svg" /> -->
</div>
<div class="blockin"> <div class="blockin">
<div class="blockico"> <div class="blockico">
<span /> <i class={blockDefinition.icon} />
<!-- <img src="assets/eye.svg" /> -->
</div> </div>
<div class="blocktext"> <div class="blocktext">
<p class="blocktitle">{blockDefinition.name}</p> <p class="blocktitle">{blockDefinition.name}</p>

View File

@ -7,44 +7,55 @@ const ACTIONS = {
}, },
NAVIGATE: { NAVIGATE: {
name: "Navigate", name: "Navigate",
icon: "", icon: "ri-navigation-line",
description: "Navigate to another page.", description: "Navigate to another page.",
type: "CLIENT" type: "CLIENT"
}, },
CREATE_RECORD: { SAVE_RECORD: {
name: "Save Record", name: "Save Record",
icon: "", icon: "ri-save-3-fill",
description: "Save a record to your database.", description: "Save a record to your database.",
type: "SERVER", type: "SERVER",
}, },
DELETE_RECORD: { DELETE_RECORD: {
description: "Delete a record from your database.", description: "Delete a record from your database.",
icon: "", icon: "ri-delete-bin-line",
name: "Delete Record", name: "Delete Record",
type: "SERVER", type: "SERVER",
} }
}; };
const TRIGGERS = { const TRIGGERS = {
SAVE_RECORD: {
name: "Record Saved",
icon: "ri-delete-bin-line",
description: "Save a record to your database.",
type: "SERVER",
},
CLICK: { CLICK: {
name: "Click", name: "Click",
icon: "", icon: "ri-cursor-line",
description: "Trigger when you click on an element in the UI." description: "Trigger when you click on an element in the UI."
}, },
LOAD: { LOAD: {
name: "Load", name: "Load",
icon: "", icon: "ri-loader-line",
description: "Trigger an element has finished loading." description: "Trigger an element has finished loading."
}, },
INPUT: { INPUT: {
name: "Input", name: "Input",
icon: "", icon: "ri-text",
description: "Trigger when you type into an input box." description: "Trigger when you type into an input box."
}, },
}; };
const UTILITIES = { const UTILITIES = {
IFELSE: {
name: "If/Else",
icon: "ri-git-branch-line",
description: "Perform different actions based on a condition",
type: "CLIENT"
},
} }
export default { export default {

View File

@ -16,6 +16,7 @@
name, name,
instanceId, instanceId,
}) })
flowy.deleteBlocks();
onClosed() onClosed()
} }
</script> </script>

View File

@ -1,10 +1,9 @@
<script> <script>
import Modal from "svelte-simple-modal" import Modal from "svelte-simple-modal"
import { onMount, getContext } from "svelte" import { onMount, getContext } from "svelte"
import { backendUiStore, workflowStore } from "builderStore"; import { backendUiStore, workflowStore } from "builderStore"
import api from "builderStore/api" import api from "builderStore/api"
import CreateWorkflowModal from "./CreateWorkflowModal.svelte"; import CreateWorkflowModal from "./CreateWorkflowModal.svelte"
const { open, close } = getContext("simple-modal") const { open, close } = getContext("simple-modal")
@ -19,7 +18,7 @@
} }
onMount(() => { onMount(() => {
workflowStore.actions.fetch($backendUiStore.selectedDatabase._id); workflowStore.actions.fetch($backendUiStore.selectedDatabase._id)
}) })
</script> </script>
@ -30,7 +29,10 @@
</header> </header>
<ul> <ul>
{#each $workflowStore.workflows as workflow} {#each $workflowStore.workflows as workflow}
<li class="workflow-item"> <li
class="workflow-item"
class:selected={workflow._id === $workflowStore.selectedWorkflowId}
on:click={() => workflowStore.actions.select(workflow)}>
<i class="ri-stackshare-line" /> <i class="ri-stackshare-line" />
{workflow.name} {workflow.name}
</li> </li>
@ -81,4 +83,8 @@
cursor: pointer; cursor: pointer;
background: var(--secondary); background: var(--secondary);
} }
.workflow-item.selected {
background: var(--secondary);
}
</style> </style>

View File

@ -1,6 +1,7 @@
<script> <script>
import { workflowStore } from "builderStore"
import { WorkflowList } from "./WorkflowList" import { WorkflowList } from "./WorkflowList"
import { BlockPanel } from "./BlockPanel"; import { BlockPanel } from "./BlockPanel"
</script> </script>
<div class="root"> <div class="root">
@ -11,7 +12,9 @@
<slot /> <slot />
</div> </div>
<div class="nav"> <div class="nav">
{#if $workflowStore.selectedWorkflowId}
<BlockPanel /> <BlockPanel />
{/if}
</div> </div>
</div> </div>

View File

@ -1,25 +1,31 @@
<script> <script>
import { onMount } from "svelte" import { onMount } from "svelte"
import { workflowStore, backendUiStore } from "builderStore";
export let workflow = {} import api from "builderStore/api";
let canvas let canvas
let workflow
let instanceId = $backendUiStore.selectedDatabase._id;
$: workflow = $workflowStore.workflows.find(wf => wf._id === $workflowStore.selectedWorkflowId)
// $: if (workflow && workflow.uiTree) flowy.import(workflow.uiTree);
onMount(() => { onMount(() => {
if (workflow.uiTree) { flowy(canvas, onGrab, onRelease, onSnap);
flowy.import(workflow.uiTree); });
return;
function onGrab(block) {
console.log(block);
} }
flowy(canvas, onGrab, onRelease); function onSnap(block, first, parent){
}) workflow.uiTree = flowy.output();
workflowStore.actions.update({ instanceId, workflow })
function onGrab() { return true;
} }
function onRelease() { function onRelease() {
console.log("RELEASED!")
} }
// function onGrab(block) { // function onGrab(block) {

View File

@ -15,8 +15,6 @@ export {
DatatableCell, DatatableCell,
DatatableRow, DatatableRow,
} from "./Datatable" } from "./Datatable"
export { default as indexDatatable } from "./Templates/indexDatatable"
export { default as recordForm } from "./Templates/recordForm"
export { List, ListItem } from "./List" export { List, ListItem } from "./List"
export { Menu } from "./Menu" export { Menu } from "./Menu"
export { Select } from "./Select" export { Select } from "./Select"

View File

@ -28,7 +28,7 @@
"test:watch": "jest -w", "test:watch": "jest -w",
"initialise": "node ../cli/bin/budi init -b local -q", "initialise": "node ../cli/bin/budi init -b local -q",
"budi": "node ../cli/bin/budi", "budi": "node ../cli/bin/budi",
"dev:builder": "nodemon ../cli/bin/budi run", "dev:builder": "node ../cli/bin/budi run",
"electron": "electron src/electron.js", "electron": "electron src/electron.js",
"build:electron": "electron-builder --dir", "build:electron": "electron-builder --dir",
"publish:electron": "electron-builder -mwl --publish always", "publish:electron": "electron-builder -mwl --publish always",

View File

@ -38,14 +38,28 @@ exports.create = async function(ctx) {
message: "Workflow created successfully", message: "Workflow created successfully",
workflow: { workflow: {
...workflow, ...workflow,
...response _rev: response.rev,
_id: response.id
} }
}; };
} }
exports.update = async function(ctx) { exports.update = async function(ctx) {
const db = new CouchDB(ctx.params.instanceId) const db = new CouchDB(ctx.params.instanceId)
ctx.body = await db.get(ctx.params.recordId) const workflow = ctx.request.body;
const response = await db.put(workflow)
workflow._rev = response.rev
ctx.status = 200
ctx.body = {
message: `Workflow ${workflow._id} updated successfully.`,
workflow: {
...workflow,
_rev: response.rev,
_id: response.id
},
}
} }
exports.fetch = async function(ctx) { exports.fetch = async function(ctx) {

View File

@ -73,6 +73,25 @@ describe("/workflows", () => {
}) })
}) })
describe("update", () => {
it("updates a workflows data", async () => {
await createWorkflow();
workflow._id = workflow.id
workflow._rev = workflow.rev
workflow.name = "Updated Name";
const res = await request
.put(`/api/${instance._id}/workflows`)
.set(defaultHeaders)
.send(workflow)
.expect('Content-Type', /json/)
.expect(200)
expect(res.body.message).toEqual("Workflow Test Workflow updated successfully.");
expect(res.body.workflow.name).toEqual("Updated Name");
})
})
describe("fetch", () => { describe("fetch", () => {
it("return all the workflows for an instance", async () => { it("return all the workflows for an instance", async () => {
await createWorkflow(); await createWorkflow();

View File

@ -7,7 +7,7 @@ router
.get("/api/:instanceId/workflows", controller.fetch) .get("/api/:instanceId/workflows", controller.fetch)
.get("/api/:instanceId/workflows/:id", controller.find) .get("/api/:instanceId/workflows/:id", controller.find)
.post("/api/:instanceId/workflows", controller.create) .post("/api/:instanceId/workflows", controller.create)
.put("/api/:instanceId/workflows/:id", controller.update) .put("/api/:instanceId/workflows", controller.update)
.delete("/api/:instanceId/workflows/:id/:rev", controller.destroy) .delete("/api/:instanceId/workflows/:id/:rev", controller.destroy)
module.exports = router module.exports = router