workflow charts
This commit is contained in:
parent
e659e69e86
commit
cacee947bf
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
name,
|
name,
|
||||||
instanceId,
|
instanceId,
|
||||||
})
|
})
|
||||||
|
flowy.deleteBlocks();
|
||||||
onClosed()
|
onClosed()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue