snapshot of https://github.com/cortezaproject/corteza.git (2024.9.x, 2026-09-18; blobs > 131072B omitted)
Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
3
client/web/workflow/.browserslistrc
Normal file
@@ -0,0 +1,3 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
10
client/web/workflow/.eslintrc.js
Normal file
@@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
extends: ['corteza-client'],
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@babel/eslint-parser',
|
||||
requireConfigFile: false,
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
},
|
||||
}
|
||||
20
client/web/workflow/.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
dist
|
||||
*.log
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.iml
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw*
|
||||
|
||||
.nyc_output
|
||||
coverage
|
||||
|
||||
/.act*
|
||||
/workflow
|
||||
42
client/web/workflow/Makefile
Normal file
@@ -0,0 +1,42 @@
|
||||
.PHONY: dep test build release upload
|
||||
|
||||
|
||||
YARN_FLAGS ?= --non-interactive --no-progress --silent --emoji false
|
||||
YARN = yarn $(YARN_FLAGS)
|
||||
|
||||
REPO_NAME ?= corteza-webapp-workflow
|
||||
|
||||
BUILD_FLAVOUR ?= corteza
|
||||
BUILD_FLAGS ?= --production
|
||||
BUILD_DEST_DIR = dist
|
||||
BUILD_TIME ?= $(shell date +%FT%T%z)
|
||||
BUILD_VERSION ?= $(shell git describe --tags --abbrev=0)
|
||||
|
||||
BUILD_NAME = $(REPO_NAME)-$(BUILD_VERSION)
|
||||
|
||||
RELEASE_NAME = $(BUILD_NAME).tar.gz
|
||||
RELEASE_EXTRA_FILES ?= README.md LICENSE CONTRIBUTING.md DCO
|
||||
RELEASE_PKEY ?= .upload-rsa
|
||||
|
||||
dep:
|
||||
$(YARN) install
|
||||
|
||||
test:
|
||||
$(YARN) lint
|
||||
$(YARN) test:unit
|
||||
|
||||
build:
|
||||
export BUILD_VERSION=${BUILD_VERSION} && $(YARN) build $(BUILD_FLAGS)
|
||||
|
||||
release:
|
||||
@ echo $(RELEASE_NAME)
|
||||
@ cp $(RELEASE_EXTRA_FILES) $(BUILD_DEST_DIR)
|
||||
@ tar -C $(BUILD_DEST_DIR) -czf $(RELEASE_NAME) $(dir $(BUILD_DEST_DIR))
|
||||
|
||||
upload: $(RELEASE_PKEY)
|
||||
@ echo "put *.tar.gz" | sftp -q -o "StrictHostKeyChecking no" -i $(RELEASE_PKEY) $(RELEASE_SFTP_URI)
|
||||
@ rm -f $(RELEASE_PKEY)
|
||||
|
||||
$(RELEASE_PKEY):
|
||||
@ echo $(RELEASE_SFTP_KEY) | base64 -d > $(RELEASE_PKEY)
|
||||
@ chmod 0400 $@
|
||||
5
client/web/workflow/babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
['@vue/app', { useBuiltIns: 'entry' }],
|
||||
],
|
||||
}
|
||||
36
client/web/workflow/jest.config.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/** @type {import('jest').Config} */
|
||||
module.exports = {
|
||||
preset: '@vue/cli-plugin-unit-jest',
|
||||
testMatch: [
|
||||
'**/tests/unit/**/*.spec.js',
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'^corteza-webapp-workflow/(.*)$': '<rootDir>/$1',
|
||||
},
|
||||
moduleFileExtensions: [
|
||||
'js',
|
||||
'jsx',
|
||||
'json',
|
||||
'vue',
|
||||
],
|
||||
transform: {
|
||||
'^.+\\.vue$': '@vue/vue2-jest',
|
||||
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
|
||||
'^.+\\.jsx?$': 'babel-jest',
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'/node_modules/(?!(chai|sinon)/)',
|
||||
],
|
||||
snapshotSerializers: [
|
||||
'jest-serializer-vue',
|
||||
],
|
||||
testEnvironment: 'jsdom',
|
||||
testURL: 'http://localhost/',
|
||||
watchPlugins: [
|
||||
'jest-watch-typeahead/filename',
|
||||
'jest-watch-typeahead/testname',
|
||||
],
|
||||
setupFiles: [
|
||||
'<rootDir>/tests/setup.js',
|
||||
],
|
||||
}
|
||||
82
client/web/workflow/package.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "corteza-webapp-workflow",
|
||||
"description": "Corteza Workflow WebApp",
|
||||
"version": "2024.9.10",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"dev-env": "vue-cli-service serve src/dev-env.js",
|
||||
"build": "vue-cli-service build --mode production",
|
||||
"lint": "vue-cli-service lint --fix",
|
||||
"cdeps": "yarn link @cortezaproject/corteza-js @cortezaproject/corteza-vue",
|
||||
"test:unit": "vue-cli-service test:unit",
|
||||
"test": "yarn test:unit"
|
||||
},
|
||||
"gitHooks": {
|
||||
"pre-commit": "yarn lint"
|
||||
},
|
||||
"stylelint": {
|
||||
"plugins": [
|
||||
"stylelint-scss"
|
||||
],
|
||||
"extends": "stylelint-config-standard",
|
||||
"rules": {
|
||||
"color-hex-length": null,
|
||||
"no-empty-source": null
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"cross-spawn": "^7.0.6",
|
||||
"postcss": "^8.5.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cortezaproject/corteza-js": "^2024.9.10",
|
||||
"@cortezaproject/corteza-vue": "2024.9.10",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/vue-fontawesome": "^2.0.10",
|
||||
"bootstrap-vue": "2.23.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"mxgraph": "^4.2.0",
|
||||
"portal-vue": "^2.1.7",
|
||||
"v-jsoneditor": "^1.4.2",
|
||||
"vue": "2.7.16",
|
||||
"vue-native-websocket": "^2.0.15",
|
||||
"vue-router": "3.1.5",
|
||||
"vuedraggable": "^2.23.0",
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.26.7",
|
||||
"@babel/eslint-parser": "^7.26.5",
|
||||
"@vue/cli-plugin-babel": "5.0.8",
|
||||
"@vue/cli-plugin-eslint": "5.0.8",
|
||||
"@vue/cli-plugin-unit-jest": "^5.0.8",
|
||||
"@vue/cli-service": "5.0.8",
|
||||
"@vue/eslint-config-standard": "^8.0.1",
|
||||
"@vue/test-utils": "^1.3.6",
|
||||
"@vue/vue2-jest": "^27.0.0",
|
||||
"babel-jest": "^27.1.0",
|
||||
"chai": "^4.3.7",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"eslint": "^8.7.2",
|
||||
"eslint-config-corteza-client": "file:../../../lib/eslint-client",
|
||||
"eslint-plugin-vue": "^9.32.0",
|
||||
"jest": "^27.1.0",
|
||||
"null-loader": "^4.0.1",
|
||||
"resolve-url-loader": "^5.0.0",
|
||||
"rtlcss": "^4.3.0",
|
||||
"sass": "^1.85.1",
|
||||
"sass-loader": "^10",
|
||||
"sinon": "^9.2.4",
|
||||
"stylelint": "16.14.1",
|
||||
"stylelint-config-standard": "^37.0.0",
|
||||
"stylelint-scss": "^6.11.0",
|
||||
"stylelint-webpack-plugin": "^5.0.1",
|
||||
"vue-eslint-parser": "^9.4.3",
|
||||
"vue-template-compiler": "2.7.16",
|
||||
"webpack": "^5.97.1"
|
||||
}
|
||||
}
|
||||
6
client/web/workflow/postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {},
|
||||
rtlcss: {},
|
||||
},
|
||||
}
|
||||
1
client/web/workflow/public/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
config.js
|
||||
12
client/web/workflow/public/config.example.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// Corteza API location
|
||||
window.CortezaAPI = 'https://api.cortezaproject.your-domain.tld';
|
||||
|
||||
// CortezaAuth can be autoconfigured by replacing /api with /auth in CortezaAPI
|
||||
// or by appending /auth to the end of CortezaAPI string
|
||||
// When this is not possible and your configuration is more exotic you can set it
|
||||
// explicitly:
|
||||
// window.CortezaAuth = 'https://api.cortezaproject.your-domain.tld/auth';
|
||||
|
||||
// Configure CortezaWebapp when your web applications are not placed on the root.
|
||||
// This is autoconfigured from the value of <base> tag href attribute in most cases.
|
||||
// window.CortezaWebapp = 'https://cortezaproject.your-domain.tld';
|
||||
9
client/web/workflow/public/icon.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.0075 -0.0058 36.9075 38.5121" width="32px" height="32px">
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
.st0{fill:#95B4AF;}
|
||||
</style>
|
||||
</defs>
|
||||
<path class="st0" d="M2.5,9.4c1.6-2.9,4-5.3,6.9-6.9c3-1.7,6.3-2.5,9.8-2.5c3.9-0.1,7.8,1.1,11.1,3.2c3.2,2.2,5.5,5.3,6.6,9h-8.7 c-0.8-1.7-2-3.1-3.6-4.1c-1.7-0.9-3.5-1.4-5.4-1.3c-2.1,0-4.1,0.5-5.9,1.5c-1.7,1-3.1,2.6-4,4.4c-1,2.1-1.5,4.3-1.4,6.6 c0,2.3,0.4,4.5,1.4,6.6c0.9,1.8,2.3,3.3,4,4.4c1.8,1,3.8,1.6,5.9,1.5c1.9,0,3.8-0.4,5.4-1.4c1.6-1,2.9-2.4,3.6-4.1h8.7 c-1.1,3.7-3.5,6.8-6.6,9c-3.3,2.2-7.2,3.3-11.1,3.2c-3.4,0-6.8-0.8-9.8-2.5c-2.9-1.6-5.2-4-6.9-6.8c-1.7-3-2.6-6.5-2.5-10 C-0.1,15.9,0.8,12.4,2.5,9.4z" id="object-0" style="fill: rgb(255, 150, 97);"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 792 B |
1
client/web/workflow/public/icons/break.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1022.25 99.354c-450.277-9.22-870.449 360.942-917.287 809.188c-49.497 379.099 158.63 778.965 505.847 944.176c327.865 164.75 753.014 115.134 1024.927-134.893c274.593-240.828 389.319-650.62 262.28-995.771c-116.8-340.195-443.475-599.282-804.796-620.01a950.215 950.215 0 0 0-70.971-2.69zm-10.482 99.76c412.893-15.55 800.952 328.748 833.306 741.03c39.736 363.606-189.04 740.293-537.188 860.127c-338.432 125.141-753.857 3.445-958.257-297.536c-209.788-293.857-201.456-727.62 36.58-1003.308c152.352-183.514 386.115-299.42 625.559-300.314zm16.396 89.923c-381.953-12.147-733.35 323.18-740.25 705.154c-16.837 345.168 238.98 683.903 580.72 749.641c316.644 69.318 670.444-93.359 811.717-387.88c146.107-287.645 80.764-670.441-168.526-880.558c-132.088-117.004-306.804-186.158-483.66-186.357zm-4.494 98.049c336.708-8.916 643.367 294.108 637.967 631.146c5.952 311.574-244.821 607.697-557.659 639.11c-293.715 38.565-603.394-149.77-687.837-437.104c-88.514-273.766 27.776-603.496 282.864-744.453c97.803-57.623 211.099-88.88 324.665-88.7zM794.723 615.498L615.498 794.723l230.63 230.633l-226.464 226.466l176.38 176.381l226.468-226.467l230.765 230.766l179.225-179.225l-230.766-230.765l226.473-226.475l-176.38-176.38c-75.492 75.49-150.983 150.981-226.473 226.472L794.723 615.498zm-.127 70.68l230.765 230.765l226.467-226.464l105.695 105.695l-226.466 226.465l230.633 230.632l-108.41 108.41l-230.632-230.632l-226.466 226.467l-105.696-105.696l226.467-226.467l-230.765-230.765l108.408-108.41z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
1
client/web/workflow/public/icons/clock-danger.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path fill="#E54122" d="M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 504 B |
1
client/web/workflow/public/icons/clock-success.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path fill="#43AA8B" d="M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 504 B |
1
client/web/workflow/public/icons/cog.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M487.4 315.7l-42.6-24.6c4.3-23.2 4.3-47 0-70.2l42.6-24.6c4.9-2.8 7.1-8.6 5.5-14-11.1-35.6-30-67.8-54.7-94.6-3.8-4.1-10-5.1-14.8-2.3L380.8 110c-17.9-15.4-38.5-27.3-60.8-35.1V25.8c0-5.6-3.9-10.5-9.4-11.7-36.7-8.2-74.3-7.8-109.2 0-5.5 1.2-9.4 6.1-9.4 11.7V75c-22.2 7.9-42.8 19.8-60.8 35.1L88.7 85.5c-4.9-2.8-11-1.9-14.8 2.3-24.7 26.7-43.6 58.9-54.7 94.6-1.7 5.4.6 11.2 5.5 14L67.3 221c-4.3 23.2-4.3 47 0 70.2l-42.6 24.6c-4.9 2.8-7.1 8.6-5.5 14 11.1 35.6 30 67.8 54.7 94.6 3.8 4.1 10 5.1 14.8 2.3l42.6-24.6c17.9 15.4 38.5 27.3 60.8 35.1v49.2c0 5.6 3.9 10.5 9.4 11.7 36.7 8.2 74.3 7.8 109.2 0 5.5-1.2 9.4-6.1 9.4-11.7v-49.2c22.2-7.9 42.8-19.8 60.8-35.1l42.6 24.6c4.9 2.8 11 1.9 14.8-2.3 24.7-26.7 43.6-58.9 54.7-94.6 1.5-5.5-.7-11.3-5.6-14.1zM256 336c-44.1 0-80-35.9-80-80s35.9-80 80-80 80 35.9 80 80-35.9 80-80 80z" fill="#4D7281"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
8
client/web/workflow/public/icons/connection-point.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100"
|
||||
height="100"
|
||||
version="1.1"
|
||||
>
|
||||
<circle cx="50" cy="50" r="25" fill="#A7D0E3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 145 B |
1
client/web/workflow/public/icons/content.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="32px" height="32px"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#0B344E" d="M432 416h-23.4L277.9 53.7A32 32 0 0 0 247.6 32h-47.2a32 32 0 0 0 -30.3 21.7L39.4 416H16a16 16 0 0 0 -16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0 -16-16h-19.6l23.3-64h152.6l23.3 64H304a16 16 0 0 0 -16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0 -16-16zM176.9 272L224 142.5 271.2 272z"/></svg>
|
||||
|
After Width: | Height: | Size: 594 B |
1
client/web/workflow/public/icons/continue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1022.25 99.36C571.973 90.17 151.801 460.306 104.963 908.553c-49.497 379.097 158.63 778.958 505.847 944.173c327.865 164.75 753.014 115.136 1024.928-134.893c274.592-240.828 389.318-650.625 262.28-995.771c-116.8-340.193-443.475-599.288-804.797-620.012c-23.612-1.742-47.291-2.71-70.97-2.71zm-10.482 99.762c412.892-15.548 800.952 328.747 833.307 741.03c39.736 363.608-189.04 740.296-537.189 860.124c-338.432 125.14-753.857 3.483-958.257-297.534c-209.788-293.858-201.456-727.62 36.58-1003.309c152.352-183.51 386.115-299.42 625.559-300.31zm16.396 89.922c-381.953-12.152-733.35 323.184-740.25 705.155c-16.837 345.166 238.98 683.897 580.72 749.641c316.644 69.314 670.444-93.357 811.717-387.882c146.108-287.647 80.764-670.44-168.525-880.558c-132.09-117.003-306.805-186.162-483.662-186.356zm-4.494 98.05c336.708-8.902 643.367 294.109 637.967 631.148c5.952 311.573-244.82 607.695-557.658 639.11c-293.716 38.566-603.396-149.773-687.84-437.11c-88.514-273.762 27.778-603.496 282.866-744.455c97.803-57.616 211.099-88.877 324.665-88.693zm102.98 236.909v205.745H664v388.53h462.65v205.716c112.45-133.325 224.9-266.65 337.35-399.986l-337.35-400.005zm56.356 149.56l208.967 248.52l-208.967 248.52v-111.876H726.938V887.45h456.068V773.563z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
client/web/workflow/public/icons/dark/break.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1022.25 99.354c-450.277-9.22-870.449 360.942-917.287 809.188c-49.497 379.099 158.63 778.965 505.847 944.176c327.865 164.75 753.014 115.134 1024.927-134.893c274.593-240.828 389.319-650.62 262.28-995.771c-116.8-340.195-443.475-599.282-804.796-620.01a950.215 950.215 0 0 0-70.971-2.69zm-10.482 99.76c412.893-15.55 800.952 328.748 833.306 741.03c39.736 363.606-189.04 740.293-537.188 860.127c-338.432 125.141-753.857 3.445-958.257-297.536c-209.788-293.857-201.456-727.62 36.58-1003.308c152.352-183.514 386.115-299.42 625.559-300.314zm16.396 89.923c-381.953-12.147-733.35 323.18-740.25 705.154c-16.837 345.168 238.98 683.903 580.72 749.641c316.644 69.318 670.444-93.359 811.717-387.88c146.107-287.645 80.764-670.441-168.526-880.558c-132.088-117.004-306.804-186.158-483.66-186.357zm-4.494 98.049c336.708-8.916 643.367 294.108 637.967 631.146c5.952 311.574-244.821 607.697-557.659 639.11c-293.715 38.565-603.394-149.77-687.837-437.104c-88.514-273.766 27.776-603.496 282.864-744.453c97.803-57.623 211.099-88.88 324.665-88.7zM794.723 615.498L615.498 794.723l230.63 230.633l-226.464 226.466l176.38 176.381l226.468-226.467l230.765 230.766l179.225-179.225l-230.766-230.765l226.473-226.475l-176.38-176.38c-75.492 75.49-150.983 150.981-226.473 226.472L794.723 615.498zm-.127 70.68l230.765 230.765l226.467-226.464l105.695 105.695l-226.466 226.465l230.633 230.632l-108.41 108.41l-230.632-230.632l-226.466 226.467l-105.696-105.696l226.467-226.467l-230.765-230.765l108.408-108.41z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
1
client/web/workflow/public/icons/dark/clock-danger.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path fill="#E54122" d="M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 504 B |
1
client/web/workflow/public/icons/dark/clock-success.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path fill="#43AA8B" d="M256,8C119,8,8,119,8,256S119,504,256,504,504,393,504,256,393,8,256,8Zm92.49,313h0l-20,25a16,16,0,0,1-22.49,2.5h0l-67-49.72a40,40,0,0,1-15-31.23V112a16,16,0,0,1,16-16h32a16,16,0,0,1,16,16V256l58,42.5A16,16,0,0,1,348.49,321Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 504 B |
1
client/web/workflow/public/icons/dark/content.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="32px" height="32px"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path fill="#FBF7F4" d="M432 416h-23.4L277.9 53.7A32 32 0 0 0 247.6 32h-47.2a32 32 0 0 0 -30.3 21.7L39.4 416H16a16 16 0 0 0 -16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0 -16-16h-19.6l23.3-64h152.6l23.3 64H304a16 16 0 0 0 -16 16v32a16 16 0 0 0 16 16h128a16 16 0 0 0 16-16v-32a16 16 0 0 0 -16-16zM176.9 272L224 142.5 271.2 272z"/></svg>
|
||||
|
After Width: | Height: | Size: 594 B |
1
client/web/workflow/public/icons/dark/continue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1022.25 99.36C571.973 90.17 151.801 460.306 104.963 908.553c-49.497 379.097 158.63 778.958 505.847 944.173c327.865 164.75 753.014 115.136 1024.928-134.893c274.592-240.828 389.318-650.625 262.28-995.771c-116.8-340.193-443.475-599.288-804.797-620.012c-23.612-1.742-47.291-2.71-70.97-2.71zm-10.482 99.762c412.892-15.548 800.952 328.747 833.307 741.03c39.736 363.608-189.04 740.296-537.189 860.124c-338.432 125.14-753.857 3.483-958.257-297.534c-209.788-293.858-201.456-727.62 36.58-1003.309c152.352-183.51 386.115-299.42 625.559-300.31zm16.396 89.922c-381.953-12.152-733.35 323.184-740.25 705.155c-16.837 345.166 238.98 683.897 580.72 749.641c316.644 69.314 670.444-93.357 811.717-387.882c146.108-287.647 80.764-670.44-168.525-880.558c-132.09-117.003-306.805-186.162-483.662-186.356zm-4.494 98.05c336.708-8.902 643.367 294.109 637.967 631.148c5.952 311.573-244.82 607.695-557.658 639.11c-293.716 38.566-603.396-149.773-687.84-437.11c-88.514-273.762 27.778-603.496 282.866-744.455c97.803-57.616 211.099-88.877 324.665-88.693zm102.98 236.909v205.745H664v388.53h462.65v205.716c112.45-133.325 224.9-266.65 337.35-399.986l-337.35-400.005zm56.356 149.56l208.967 248.52l-208.967 248.52v-111.876H726.938V887.45h456.068V773.563z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
client/web/workflow/public/icons/dark/debug.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bug" class="svg-inline--fa fa-bug fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" width="28px" height="28px" viewBox="0 0 512 512"><path fill="#FBF7F4" d="M511.988 288.9c-.478 17.43-15.217 31.1-32.653 31.1H424v16c0 21.864-4.882 42.584-13.6 61.145l60.228 60.228c12.496 12.497 12.496 32.758 0 45.255-12.498 12.497-32.759 12.496-45.256 0l-54.736-54.736C345.886 467.965 314.351 480 280 480V236c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v244c-34.351 0-65.886-12.035-90.636-32.108l-54.736 54.736c-12.498 12.497-32.759 12.496-45.256 0-12.496-12.497-12.496-32.758 0-45.255l60.228-60.228C92.882 378.584 88 357.864 88 336v-16H32.666C15.23 320 .491 306.33.013 288.9-.484 270.816 14.028 256 32 256h56v-58.745l-46.628-46.628c-12.496-12.497-12.496-32.758 0-45.255 12.498-12.497 32.758-12.497 45.256 0L141.255 160h229.489l54.627-54.627c12.498-12.497 32.758-12.497 45.256 0 12.496 12.497 12.496 32.758 0 45.255L424 197.255V256h56c17.972 0 32.484 14.816 31.988 32.9zM257 0c-61.856 0-112 50.144-112 112h224C369 50.144 318.856 0 257 0z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
client/web/workflow/public/icons/dark/delay.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1024 99.004c-510.27 0-925 414.72-925 924.994s414.729 925.004 925 925.004s925-414.73 925-925.004c0-510.273-414.73-924.994-925-924.994zm0 100c456.228 0 825 368.764 825 824.994s-368.773 825.004-825 825.004s-825-368.774-825-825.004s368.772-824.994 825-824.994zm-8.29 385.945c-171.528 1.212-335.1 113.885-398.317 273.285c-65.79 155.801-29.42 347.57 88.884 468.442c115.027 123.83 304.555 169.464 463.307 111.428c166.36-56.675 288.573-221.231 292.976-397.19c8.863-170.945-93.874-339.97-248.558-412.566c-59.027-28.58-124.545-43.458-190.117-43.371a394.838 394.838 0 0 0-8.174-.028zm15.643 79.973c156.707.7 303.715 116.52 340.258 269.176c40.7 149.018-27.87 318.936-161.361 397.015c-136.3 85.544-328.47 62.745-440.395-53.11c-118.113-113.943-139.147-311.084-48.53-447.733c64.178-101.947 182.05-166.467 302.56-165.293c2.492-.048 4.981-.066 7.468-.055zm105.952 102.072l-119.903 217.527c-37.032 3.938-45.83 60.122-11.662 75.106c22.57 15.666 40.247-10.392 60.793-9.494h183.293v-50H1065.89l115.205-209.004l-43.79-24.135z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
client/web/workflow/public/icons/dark/error-handler.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1024 99c-363.757-4.934-714.249 229.17-852.226 565.192c-144.118 330.153-69.236 740.283 182.862 997.771c242.464 260.992 641.95 356.786 976.52 234.67c346.74-118.073 602.724-458.592 616.639-825.09c23.34-360.131-188.536-719.422-512.954-876.293C1307.807 131.856 1165.95 98.797 1024 99zm0 100c359.283-6.249 700.984 251.836 793.747 598.93c97.03 326.908-34.215 705.405-315.907 899.024C1209.227 1912.03 775.3 1896.47 498.977 1660.8c-278.414-221.98-378.073-632.576-235.228-958.182C387.557 401.605 698.338 195.179 1024 199zm354.53 430.836l-210.237 426.18l-261.406-340.014l-237.416 702.162l266.172-342.129l274.562 314.17l168.324-760.37zm-448.397 224.86l248.558 315.126l62.078-138.195l-59.431 239.111l-253.695-298.441l-102.926 159.117l105.416-276.719z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
client/web/workflow/public/icons/dark/error.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1005.303 98.988c-447.59.377-859.063 373.342-901.291 819.483c-45.314 379.987 168.535 777.154 517.567 938.558c324.137 158.423 740.176 108.288 1009.01-135.647c273.526-236.69 393.041-640.13 272.893-984.32c-114.258-351.15-451.46-619.981-822.842-636.252c-25.079-1.64-50.213-1.947-75.337-1.822zm18.334 289.733c329.628-9.588 631.825 281.137 635.693 610.898c14.255 301.673-212.385 597.907-513.018 648.484c-290.913 58.275-613.563-109.854-715.39-392.282c-109.622-279.29 2.217-632.478 269.045-779.065c97.56-57.284 210.508-88.176 323.67-88.035zm354.892 241.117l-210.236 426.178L906.887 716L669.47 1418.162l266.174-342.127l274.562 314.172l168.322-760.37z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
client/web/workflow/public/icons/dark/exec-workflow.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><g transform="translate(0 995.638)"><rect width="1699.302" height="1400.778" x="174.349" y="-672.027" rx="266.951" fill="none" stroke="#FBF7F4" stroke-width="100" stroke-linecap="round" stroke-linejoin="round"/></g><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 563 B |
10
client/web/workflow/public/icons/dark/expressions.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<g id="Group_6221" data-name="Group 6221" transform="translate(-378 -5834)">
|
||||
<rect id="Rectangle_261" data-name="Rectangle 261" width="32" height="32" transform="translate(378 5834)" fill="rgba(255,255,255,0)"/>
|
||||
<g id="Group_6219" data-name="Group 6219" transform="translate(381 5845)">
|
||||
<path id="Path_5690" data-name="Path 5690" d="M6.877,0,4.709,3.643,2.382,0H0L3.424,5.353,0,10.613H2.312L4.461,6.991l2.311,3.623H9.148L5.746,5.3,9.17,0Z" fill="#FBF7F4"/>
|
||||
<path id="Path_5691" data-name="Path 5691" d="M10.855,0V4H6.908V5.882h3.947v4h1.973v-4h3.949V4H12.828V0Z" transform="translate(3.815)" fill="#FBF7F4"/>
|
||||
<path id="Path_5692" data-name="Path 5692" d="M14.266,0V1.9h1.527v8.717h2.044V0Z" transform="translate(7.877)" fill="#FBF7F4"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 872 B |
18
client/web/workflow/public/icons/dark/function.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32">
|
||||
<defs>
|
||||
<clipPath id="clip-path">
|
||||
<rect id="Rectangle_262" data-name="Rectangle 262" width="26.102" height="16.706" fill="#FBF7F4"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="Group_6220" data-name="Group 6220" transform="translate(-333 -5834)">
|
||||
<rect id="Rectangle_260" data-name="Rectangle 260" width="32" height="32" transform="translate(333 5834)" fill="rgba(255,255,255,0)"/>
|
||||
<g id="Group_6218" data-name="Group 6218" transform="translate(336 5842)">
|
||||
<g id="Group_6217" data-name="Group 6217" transform="translate(0 0)" clip-path="url(#clip-path)">
|
||||
<path id="Path_5686" data-name="Path 5686" d="M5.664,5.292v-1.6H3.719v-.6a1.556,1.556,0,0,1,.344-1.149A1.882,1.882,0,0,1,5.353,1.6h.176V0H5.353A4.067,4.067,0,0,0,2.684.736a2.941,2.941,0,0,0-.874,2.35v.6H.609v1.6h1.2V13.62a1.565,1.565,0,0,1-.343,1.149A1.9,1.9,0,0,1,.176,15.1H0v1.606H.176a4.064,4.064,0,0,0,2.669-.736,2.937,2.937,0,0,0,.874-2.35V5.292Z" transform="translate(0 0.001)" fill="#FBF7F4"/>
|
||||
<path id="Path_5687" data-name="Path 5687" d="M8.146,12.7a10.534,10.534,0,0,1,0-8.562A9.09,9.09,0,0,1,10.9.658L10.971.6V.22H9.411L9.365.254A8.615,8.615,0,0,0,6.681,3.667a11.287,11.287,0,0,0-1,4.745,11.322,11.322,0,0,0,1,4.751,8.6,8.6,0,0,0,2.684,3.42l.045.034h1.561v-.385l-.068-.054A9,9,0,0,1,8.146,12.7" transform="translate(2.328 0.091)" fill="#FBF7F4"/>
|
||||
<path id="Path_5688" data-name="Path 5688" d="M14.359,7.428l3.108-4.813H15.386L13.417,5.924,11.3,2.616H9.143l3.106,4.862-3.11,4.777h2.1l1.952-3.29,2.1,3.29h2.16Z" transform="translate(3.744 1.072)" fill="#FBF7F4"/>
|
||||
<path id="Path_5689" data-name="Path 5689" d="M19.054,3.667A8.612,8.612,0,0,0,16.371.254L16.325.22H14.764V.6l.066.054A9.213,9.213,0,0,1,17.583,4.14a10.489,10.489,0,0,1,0,8.562,9.082,9.082,0,0,1-2.753,3.475l-.066.054v.385h1.561l.047-.034a8.6,8.6,0,0,0,2.683-3.42,11.31,11.31,0,0,0,1-4.751,11.275,11.275,0,0,0-1-4.745" transform="translate(6.048 0.091)" fill="#FBF7F4"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1024.022 99.36c-19.324-.017-38.646 7.15-52.98 21.55L120.937 971.023c-28.67 28.668-28.537 77.295.132 105.963l849.971 849.965c28.67 28.678 77.294 28.804 105.963 0l850.106-850.1c28.669-28.667 28.536-77.296-.135-105.964L1077.002 120.91c-14.334-14.334-33.657-21.534-52.98-21.55zm-.065 126.045l798.66 798.666l-798.66 798.657l-798.66-798.657l798.66-798.666zM725.686 669.792c-.014 0-9.612 1.838-9.62 1.838c-.01 0-8.144 5.513-8.15 5.513l-30.732 30.739c-.01 0-5.61 8.225-5.614 8.322c0 .01-1.737 9.48-1.736 9.48c0 .01 1.868 9.385 1.871 9.385c0 .01 5.338 8.322 5.344 8.322l280.707 280.7l-280.572 280.574v-.088c0 .01-5.61 8.32-5.614 8.32c0 .01-1.736 9.483-1.736 9.483c0 .02 1.868 9.385 1.871 9.385c0 0 5.339 8.223 5.344 8.32l30.734 30.728c.01.01 8.411 5.516 8.418 5.516c.01 0 9.346 1.838 9.354 1.838c.01 0 9.479-1.74 9.486-1.74c.01 0 8.28-5.614 8.285-5.614l280.576-280.582l280.637 280.641c.01.01 8.412 5.516 8.418 5.516c.01 0 9.346 1.838 9.354 1.838c.01 0 9.48-1.743 9.488-1.743c.01 0 8.276-5.61 8.281-5.61l30.735-30.73c.01-.01 5.475-8.126 5.48-8.126c0-.01 1.871-9.58 1.871-9.676c0-.01-1.869-9.385-1.873-9.385c0 0-5.472-8.418-5.478-8.418l-280.606-280.611l280.608-280.604c.01 0 5.473-8.127 5.478-8.127c0-.01 1.871-9.578 1.871-9.578c0-.02-1.868-9.385-1.87-9.385c0-.01-5.606-8.322-5.612-8.322l-30.735-30.738c-.01 0-8.143-5.514-8.15-5.514c-.01 0-9.345-1.84-9.353-1.84c-.01 0-9.613 1.84-9.62 1.84c-.01 0-8.145 5.514-8.15 5.514l-280.613 280.613l-280.739-280.748v-.088c-.01 0-8.278-5.32-8.285-5.32c-.01 0-9.34-1.837-9.351-1.838h-.002z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M1024.022 99.36c-19.324-.017-38.646 7.15-52.98 21.55L120.937 971.023c-28.67 28.668-28.537 77.295.132 105.963l849.971 849.965c28.67 28.678 77.294 28.804 105.963 0l850.106-850.1c28.669-28.667 28.536-77.296-.135-105.964L1077.002 120.91c-14.334-14.334-33.657-21.534-52.98-21.55zm-.065 126.045l798.66 798.666l-798.66 798.657l-798.66-798.657l798.66-798.666zm.043 368.6c-237.232 0-430 192.78-430 430.008c0 237.228 192.768 430 430 430s430-192.772 430-430c0-237.229-192.768-430.008-430-430.008zm0 47.69c211.408 0 382.323 170.912 382.323 382.318c0 211.405-170.915 382.33-382.323 382.33c-211.407 0-382.322-170.925-382.322-382.33c0-211.406 170.915-382.319 382.322-382.319z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M1024.022 99.36c-19.324-.017-38.646 7.15-52.98 21.55L120.937 971.023c-28.67 28.668-28.537 77.295.132 105.963l849.971 849.965c28.67 28.678 77.294 28.804 105.963 0l850.106-850.1c28.669-28.667 28.536-77.296-.135-105.964L1077.002 120.91c-14.334-14.334-33.657-21.534-52.98-21.55zm-.065 126.045l798.66 798.666l-798.66 798.657l-798.66-798.657l798.66-798.666zm-21.803 329.82c0 .01-9.657 1.838-9.662 1.838c-.01 0-7.908 5.323-7.914 5.323c-.01.01-5.497 8.127-5.502 8.127c0 .01-1.86 9.675-1.863 9.675V977.04H580.188l-.067-.078c0 .01-9.618 2.129-9.623 2.129c-.01.01-7.907 5.322-7.912 5.322l.008-.098c-.01.01-5.497 8.127-5.502 8.127c-.01 0-1.861 9.676-1.865 9.676v43.47s1.848 9.783 1.914 9.85c0 .01 5.478 7.934 5.478 7.934c.01.01 7.957 5.322 7.96 5.322c.01.01 9.656 2.127 9.66 2.127h396.978v396.785l-.063-.058c0 .01 1.916 9.85 1.915 9.85c0 .01 5.476 7.933 5.476 7.933c.01.01 7.96 5.32 7.961 5.32c0 0 9.59 2.032 9.662 2.13l43.461-.01c.011 0 9.846-2.032 9.852-2.032c0 0 7.908-5.32 7.914-5.32c.01-.01 5.47-7.936 5.476-7.936c0 0 1.887-9.82 1.89-9.82V1070.86h396.88c.011.01 9.847-2.031 9.851-2.031c.01 0 7.91-5.322 7.914-5.322c.01-.01 5.471-7.934 5.477-7.934c0 0 1.886-9.82 1.89-9.82v-43.461c0-.01-1.877-9.58-1.874-9.676c-.01-.01-5.45-8.127-5.518-8.127c-.01-.01-7.958-5.32-7.96-5.32c0 .01-9.82-2.13-9.825-2.033h-396.838V580.294c0-.01-1.878-9.577-1.875-9.674c-.01-.01-5.45-8.129-5.45-8.129c-.01-.01-7.956-5.32-7.958-5.32c-.01 0-9.848-1.936-9.852-1.936l-43.469-.01z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
13
client/web/workflow/public/icons/dark/grid.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg width="8192" height="8192" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="smallGrid" width="8" height="8" patternUnits="userSpaceOnUse">
|
||||
<path d="M 8 0 L 0 0 0 8" fill="none" stroke="#3E5A6F" stroke-width="0.5"/>
|
||||
</pattern>
|
||||
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<rect width="80" height="80" fill="url(#smallGrid)"/>
|
||||
<path d="M 80 0 L 0 0 0 80" fill="none" stroke="#3E5A6F" stroke-width="1"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<rect width="8192" height="8192" fill="url(#grid)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 561 B |
5
client/web/workflow/public/icons/dark/iterator.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg)" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2000 2000">
|
||||
<path d="M429.65 210C214.934 210 40 384.196 40 598.5v803c0 214.304 174.934 388.5 389.65 388.5h1140.7c214.716 0 389.65-174.196 389.65-388.5v-803c0-214.304-174.934-388.5-389.65-388.5H429.65zm0 120h1140.7C1720.887 330 1840 448.826 1840 598.5v803c0 149.674-119.113 268.5-269.65 268.5H429.65C279.113 1670 160 1551.174 160 1401.5v-803C160 448.826 279.113 330 429.65 330z" fill="#FBF7F4"/>
|
||||
<path d="M1057.07 410.836C805.11 407.3 563.447 583.065 491.134 824.983c-55.584 173.977-23.105 373.061 85.522 520.027l-269.086-52.09l-22.804 117.814l483.865 93.664l93.687-479.986l-117.779-22.988l-56.861 291.316c-138.733-165.6-136.73-427.773 4.367-591.379c137.417-171.716 399.203-221.007 590.733-114c183.232 94.568 284.888 318.597 234.896 518.746c-44.77 208.91-247.404 367.34-460.604 363.053c-55.485-3.935-83.374 76.196-37.436 107.561c40.104 24.986 90.846 7.364 134.808 4.475c248.181-37.748 457.52-249.452 489.52-498.91c36.994-238.025-91.384-488.935-304.803-600.241c-86.168-46.769-184.068-71.364-282.089-71.21z" fill="#FBF7F4"/>
|
||||
<rect x="0" y="0" width="2000" height="2000" fill="rgba(0, 0, 0, 0)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
client/web/workflow/public/icons/dark/prompt.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2000 2000"><g transform="translate(0 947.638)"><rect width="1800" height="1460" x="100" y="-677.638" rx="329.651" ry="328.5" fill="transparent" stroke="#FBF7F4" stroke-width="120" stroke-linecap="round"/><path d="M782.99 94.728h320.662c31.894 0 46.426-18.387 52.19-46.737c8.96-43.893-13.184-85.435-52.814-85.533c-97.378-.489-320.04-.196-320.04-.196v.223s340.368.268 379.564 0c39.195-.27 53.287-33.884 53.405-71.521c.12-37.54-18.907-70.744-53.287-70.793c-117.153-.098-379.68-.635-379.68-.635v-.194s199.412.465 285.185.465c35.051-.098 53.445-30.314 53.327-68.293c-.237-37.197-19.42-63.45-53.722-63.45c-137.086-.244-428.878 1.895-437.468-.146c-2.274-.54-4.22-4.971-2.723-6.794c14.054-17.122 80.562-79.478 95.325-98.59c18.512-23.803 20.486-56.943 6.394-78.401c-16.341-24.977-38.406-24.44-58.418-11.536c-57.669 37.344-260.516 178.067-305.237 208.763c-35.525 24.39-59.84 61.245-74.287 107.045c-14.604 46.435-13.105 100.51-12.947 147.875c.158 35.095.949 63.478 9.554 98.573c24.827 102.01 90.39 154.164 186.464 154.457c182.282.684 364.643.929 546.964 0c29.801-.195 46.221-19.702 46.655-55.482c.474-36.854-16.696-58.591-46.695-58.787c-70.181-.489-238.37 0-238.37 0z" fill="none" stroke="#FBF7F4" stroke-width="43.663900000000005" stroke-linecap="round"/></g><rect x="0" y="0" width="2000" height="2000" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
client/web/workflow/public/icons/dark/swimlane.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M96 402v1280h1856V402zm1756 97.7v1091.542H480V499.702l1372-.003zM192 499.7h192v1091.541H192z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 467 B |
1
client/web/workflow/public/icons/dark/termination.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1005.303 98.988c-447.59.377-859.063 373.342-901.291 819.483c-45.314 379.987 168.535 777.154 517.567 938.558c324.137 158.423 740.176 108.288 1009.01-135.647c273.526-236.69 393.041-640.13 272.893-984.32c-114.258-351.15-451.46-619.981-822.842-636.252c-25.079-1.64-50.213-1.947-75.337-1.822zm18.334 289.733c329.628-9.588 631.825 281.137 635.693 610.898c14.255 301.673-212.385 597.907-513.018 648.484c-290.913 58.275-613.563-109.854-715.39-392.282c-109.622-279.29 2.217-632.478 269.045-779.065c97.56-57.284 210.508-88.176 323.67-88.035zM1024 554.17c-304.682-11.568-549.567 321.3-448.846 608.674c79.046 294.298 469.266 430.4 714.132 248.923c257.896-162.49 272.52-575.472 26.765-755.798c-82.202-65.575-186.913-102.06-292.051-101.8z" fill="#FBF7F4"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
client/web/workflow/public/icons/dark/trigger.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M1899 1023.999c0-483.252-391.75-874.995-875-874.995S149 540.747 149 1024c0 483.251 391.75 875.004 875 875.004s875-391.753 875-875.004z" fill="none" stroke="#FBF7F4" stroke-width="100.04248826"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 551 B |
1
client/web/workflow/public/icons/debug.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="bug" class="svg-inline--fa fa-bug fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" width="28px" height="28px" viewBox="0 0 512 512"><path fill="#0B344E" d="M511.988 288.9c-.478 17.43-15.217 31.1-32.653 31.1H424v16c0 21.864-4.882 42.584-13.6 61.145l60.228 60.228c12.496 12.497 12.496 32.758 0 45.255-12.498 12.497-32.759 12.496-45.256 0l-54.736-54.736C345.886 467.965 314.351 480 280 480V236c0-6.627-5.373-12-12-12h-24c-6.627 0-12 5.373-12 12v244c-34.351 0-65.886-12.035-90.636-32.108l-54.736 54.736c-12.498 12.497-32.759 12.496-45.256 0-12.496-12.497-12.496-32.758 0-45.255l60.228-60.228C92.882 378.584 88 357.864 88 336v-16H32.666C15.23 320 .491 306.33.013 288.9-.484 270.816 14.028 256 32 256h56v-58.745l-46.628-46.628c-12.496-12.497-12.496-32.758 0-45.255 12.498-12.497 32.758-12.497 45.256 0L141.255 160h229.489l54.627-54.627c12.498-12.497 32.758-12.497 45.256 0 12.496 12.497 12.496 32.758 0 45.255L424 197.255V256h56c17.972 0 32.484 14.816 31.988 32.9zM257 0c-61.856 0-112 50.144-112 112h224C369 50.144 318.856 0 257 0z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
client/web/workflow/public/icons/delay.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1024 99.004c-510.27 0-925 414.72-925 924.994s414.729 925.004 925 925.004s925-414.73 925-925.004c0-510.273-414.73-924.994-925-924.994zm0 100c456.228 0 825 368.764 825 824.994s-368.773 825.004-825 825.004s-825-368.774-825-825.004s368.772-824.994 825-824.994zm-8.29 385.945c-171.528 1.212-335.1 113.885-398.317 273.285c-65.79 155.801-29.42 347.57 88.884 468.442c115.027 123.83 304.555 169.464 463.307 111.428c166.36-56.675 288.573-221.231 292.976-397.19c8.863-170.945-93.874-339.97-248.558-412.566c-59.027-28.58-124.545-43.458-190.117-43.371a394.838 394.838 0 0 0-8.174-.028zm15.643 79.973c156.707.7 303.715 116.52 340.258 269.176c40.7 149.018-27.87 318.936-161.361 397.015c-136.3 85.544-328.47 62.745-440.395-53.11c-118.113-113.943-139.147-311.084-48.53-447.733c64.178-101.947 182.05-166.467 302.56-165.293c2.492-.048 4.981-.066 7.468-.055zm105.952 102.072l-119.903 217.527c-37.032 3.938-45.83 60.122-11.662 75.106c22.57 15.666 40.247-10.392 60.793-9.494h183.293v-50H1065.89l115.205-209.004l-43.79-24.135z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
client/web/workflow/public/icons/error-handler.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1024 99c-363.757-4.934-714.249 229.17-852.226 565.192c-144.118 330.153-69.236 740.283 182.862 997.771c242.464 260.992 641.95 356.786 976.52 234.67c346.74-118.073 602.724-458.592 616.639-825.09c23.34-360.131-188.536-719.422-512.954-876.293C1307.807 131.856 1165.95 98.797 1024 99zm0 100c359.283-6.249 700.984 251.836 793.747 598.93c97.03 326.908-34.215 705.405-315.907 899.024C1209.227 1912.03 775.3 1896.47 498.977 1660.8c-278.414-221.98-378.073-632.576-235.228-958.182C387.557 401.605 698.338 195.179 1024 199zm354.53 430.836l-210.237 426.18l-261.406-340.014l-237.416 702.162l266.172-342.129l274.562 314.17l168.324-760.37zm-448.397 224.86l248.558 315.126l62.078-138.195l-59.431 239.111l-253.695-298.441l-102.926 159.117l105.416-276.719z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
client/web/workflow/public/icons/error.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1005.303 98.988c-447.59.377-859.063 373.342-901.291 819.483c-45.314 379.987 168.535 777.154 517.567 938.558c324.137 158.423 740.176 108.288 1009.01-135.647c273.526-236.69 393.041-640.13 272.893-984.32c-114.258-351.15-451.46-619.981-822.842-636.252c-25.079-1.64-50.213-1.947-75.337-1.822zm18.334 289.733c329.628-9.588 631.825 281.137 635.693 610.898c14.255 301.673-212.385 597.907-513.018 648.484c-290.913 58.275-613.563-109.854-715.39-392.282c-109.622-279.29 2.217-632.478 269.045-779.065c97.56-57.284 210.508-88.176 323.67-88.035zm354.892 241.117l-210.236 426.178L906.887 716L669.47 1418.162l266.174-342.127l274.562 314.172l168.322-760.37z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
client/web/workflow/public/icons/exec-workflow.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><g transform="translate(0 995.638)"><rect width="1699.302" height="1400.778" x="174.349" y="-672.027" rx="266.951" fill="none" stroke="#0B344E" stroke-width="100" stroke-linecap="round" stroke-linejoin="round"/></g><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 563 B |
10
client/web/workflow/public/icons/expressions.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
|
||||
<g id="Group_6221" data-name="Group 6221" transform="translate(-378 -5834)">
|
||||
<rect id="Rectangle_261" data-name="Rectangle 261" width="32" height="32" transform="translate(378 5834)" fill="rgba(255,255,255,0)"/>
|
||||
<g id="Group_6219" data-name="Group 6219" transform="translate(381 5845)">
|
||||
<path id="Path_5690" data-name="Path 5690" d="M6.877,0,4.709,3.643,2.382,0H0L3.424,5.353,0,10.613H2.312L4.461,6.991l2.311,3.623H9.148L5.746,5.3,9.17,0Z" fill="#0B344E"/>
|
||||
<path id="Path_5691" data-name="Path 5691" d="M10.855,0V4H6.908V5.882h3.947v4h1.973v-4h3.949V4H12.828V0Z" transform="translate(3.815)" fill="#0B344E"/>
|
||||
<path id="Path_5692" data-name="Path 5692" d="M14.266,0V1.9h1.527v8.717h2.044V0Z" transform="translate(7.877)" fill="#0B344E"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 872 B |
18
client/web/workflow/public/icons/function.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 32 32">
|
||||
<defs>
|
||||
<clipPath id="clip-path">
|
||||
<rect id="Rectangle_262" data-name="Rectangle 262" width="26.102" height="16.706" fill="#0B344E"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="Group_6220" data-name="Group 6220" transform="translate(-333 -5834)">
|
||||
<rect id="Rectangle_260" data-name="Rectangle 260" width="32" height="32" transform="translate(333 5834)" fill="rgba(255,255,255,0)"/>
|
||||
<g id="Group_6218" data-name="Group 6218" transform="translate(336 5842)">
|
||||
<g id="Group_6217" data-name="Group 6217" transform="translate(0 0)" clip-path="url(#clip-path)">
|
||||
<path id="Path_5686" data-name="Path 5686" d="M5.664,5.292v-1.6H3.719v-.6a1.556,1.556,0,0,1,.344-1.149A1.882,1.882,0,0,1,5.353,1.6h.176V0H5.353A4.067,4.067,0,0,0,2.684.736a2.941,2.941,0,0,0-.874,2.35v.6H.609v1.6h1.2V13.62a1.565,1.565,0,0,1-.343,1.149A1.9,1.9,0,0,1,.176,15.1H0v1.606H.176a4.064,4.064,0,0,0,2.669-.736,2.937,2.937,0,0,0,.874-2.35V5.292Z" transform="translate(0 0.001)" fill="#0B344E"/>
|
||||
<path id="Path_5687" data-name="Path 5687" d="M8.146,12.7a10.534,10.534,0,0,1,0-8.562A9.09,9.09,0,0,1,10.9.658L10.971.6V.22H9.411L9.365.254A8.615,8.615,0,0,0,6.681,3.667a11.287,11.287,0,0,0-1,4.745,11.322,11.322,0,0,0,1,4.751,8.6,8.6,0,0,0,2.684,3.42l.045.034h1.561v-.385l-.068-.054A9,9,0,0,1,8.146,12.7" transform="translate(2.328 0.091)" fill="#0B344E"/>
|
||||
<path id="Path_5688" data-name="Path 5688" d="M14.359,7.428l3.108-4.813H15.386L13.417,5.924,11.3,2.616H9.143l3.106,4.862-3.11,4.777h2.1l1.952-3.29,2.1,3.29h2.16Z" transform="translate(3.744 1.072)" fill="#0B344E"/>
|
||||
<path id="Path_5689" data-name="Path 5689" d="M19.054,3.667A8.612,8.612,0,0,0,16.371.254L16.325.22H14.764V.6l.066.054A9.213,9.213,0,0,1,17.583,4.14a10.489,10.489,0,0,1,0,8.562,9.082,9.082,0,0,1-2.753,3.475l-.066.054v.385h1.561l.047-.034a8.6,8.6,0,0,0,2.683-3.42,11.31,11.31,0,0,0,1-4.751,11.275,11.275,0,0,0-1-4.745" transform="translate(6.048 0.091)" fill="#0B344E"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
1
client/web/workflow/public/icons/gateway-exclusive.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1024.022 99.36c-19.324-.017-38.646 7.15-52.98 21.55L120.937 971.023c-28.67 28.668-28.537 77.295.132 105.963l849.971 849.965c28.67 28.678 77.294 28.804 105.963 0l850.106-850.1c28.669-28.667 28.536-77.296-.135-105.964L1077.002 120.91c-14.334-14.334-33.657-21.534-52.98-21.55zm-.065 126.045l798.66 798.666l-798.66 798.657l-798.66-798.657l798.66-798.666zM725.686 669.792c-.014 0-9.612 1.838-9.62 1.838c-.01 0-8.144 5.513-8.15 5.513l-30.732 30.739c-.01 0-5.61 8.225-5.614 8.322c0 .01-1.737 9.48-1.736 9.48c0 .01 1.868 9.385 1.871 9.385c0 .01 5.338 8.322 5.344 8.322l280.707 280.7l-280.572 280.574v-.088c0 .01-5.61 8.32-5.614 8.32c0 .01-1.736 9.483-1.736 9.483c0 .02 1.868 9.385 1.871 9.385c0 0 5.339 8.223 5.344 8.32l30.734 30.728c.01.01 8.411 5.516 8.418 5.516c.01 0 9.346 1.838 9.354 1.838c.01 0 9.479-1.74 9.486-1.74c.01 0 8.28-5.614 8.285-5.614l280.576-280.582l280.637 280.641c.01.01 8.412 5.516 8.418 5.516c.01 0 9.346 1.838 9.354 1.838c.01 0 9.48-1.743 9.488-1.743c.01 0 8.276-5.61 8.281-5.61l30.735-30.73c.01-.01 5.475-8.126 5.48-8.126c0-.01 1.871-9.58 1.871-9.676c0-.01-1.869-9.385-1.873-9.385c0 0-5.472-8.418-5.478-8.418l-280.606-280.611l280.608-280.604c.01 0 5.473-8.127 5.478-8.127c0-.01 1.871-9.578 1.871-9.578c0-.02-1.868-9.385-1.87-9.385c0-.01-5.606-8.322-5.612-8.322l-30.735-30.738c-.01 0-8.143-5.514-8.15-5.514c-.01 0-9.345-1.84-9.353-1.84c-.01 0-9.613 1.84-9.62 1.84c-.01 0-8.145 5.514-8.15 5.514l-280.613 280.613l-280.739-280.748v-.088c-.01 0-8.278-5.32-8.285-5.32c-.01 0-9.34-1.837-9.351-1.838h-.002z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
1
client/web/workflow/public/icons/gateway-inclusive.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M1024.022 99.36c-19.324-.017-38.646 7.15-52.98 21.55L120.937 971.023c-28.67 28.668-28.537 77.295.132 105.963l849.971 849.965c28.67 28.678 77.294 28.804 105.963 0l850.106-850.1c28.669-28.667 28.536-77.296-.135-105.964L1077.002 120.91c-14.334-14.334-33.657-21.534-52.98-21.55zm-.065 126.045l798.66 798.666l-798.66 798.657l-798.66-798.657l798.66-798.666zm.043 368.6c-237.232 0-430 192.78-430 430.008c0 237.228 192.768 430 430 430s430-192.772 430-430c0-237.229-192.768-430.008-430-430.008zm0 47.69c211.408 0 382.323 170.912 382.323 382.318c0 211.405-170.915 382.33-382.323 382.33c-211.407 0-382.322-170.925-382.322-382.33c0-211.406 170.915-382.319 382.322-382.319z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
1
client/web/workflow/public/icons/gateway-parallel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M1024.022 99.36c-19.324-.017-38.646 7.15-52.98 21.55L120.937 971.023c-28.67 28.668-28.537 77.295.132 105.963l849.971 849.965c28.67 28.678 77.294 28.804 105.963 0l850.106-850.1c28.669-28.667 28.536-77.296-.135-105.964L1077.002 120.91c-14.334-14.334-33.657-21.534-52.98-21.55zm-.065 126.045l798.66 798.666l-798.66 798.657l-798.66-798.657l798.66-798.666zm-21.803 329.82c0 .01-9.657 1.838-9.662 1.838c-.01 0-7.908 5.323-7.914 5.323c-.01.01-5.497 8.127-5.502 8.127c0 .01-1.86 9.675-1.863 9.675V977.04H580.188l-.067-.078c0 .01-9.618 2.129-9.623 2.129c-.01.01-7.907 5.322-7.912 5.322l.008-.098c-.01.01-5.497 8.127-5.502 8.127c-.01 0-1.861 9.676-1.865 9.676v43.47s1.848 9.783 1.914 9.85c0 .01 5.478 7.934 5.478 7.934c.01.01 7.957 5.322 7.96 5.322c.01.01 9.656 2.127 9.66 2.127h396.978v396.785l-.063-.058c0 .01 1.916 9.85 1.915 9.85c0 .01 5.476 7.933 5.476 7.933c.01.01 7.96 5.32 7.961 5.32c0 0 9.59 2.032 9.662 2.13l43.461-.01c.011 0 9.846-2.032 9.852-2.032c0 0 7.908-5.32 7.914-5.32c.01-.01 5.47-7.936 5.476-7.936c0 0 1.887-9.82 1.89-9.82V1070.86h396.88c.011.01 9.847-2.031 9.851-2.031c.01 0 7.91-5.322 7.914-5.322c.01-.01 5.471-7.934 5.477-7.934c0 0 1.886-9.82 1.89-9.82v-43.461c0-.01-1.877-9.58-1.874-9.676c-.01-.01-5.45-8.127-5.518-8.127c-.01-.01-7.958-5.32-7.96-5.32c0 .01-9.82-2.13-9.825-2.033h-396.838V580.294c0-.01-1.878-9.577-1.875-9.674c-.01-.01-5.45-8.129-5.45-8.129c-.01-.01-7.956-5.32-7.958-5.32c-.01 0-9.848-1.936-9.852-1.936l-43.469-.01z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
13
client/web/workflow/public/icons/grid.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg width="8192" height="8192" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<pattern id="smallGrid" width="8" height="8" patternUnits="userSpaceOnUse">
|
||||
<path d="M 8 0 L 0 0 0 8" fill="none" stroke="#E4E9EF" stroke-width="0.5"/>
|
||||
</pattern>
|
||||
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<rect width="80" height="80" fill="url(#smallGrid)"/>
|
||||
<path d="M 80 0 L 0 0 0 80" fill="none" stroke="#E4E9EF" stroke-width="1"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<rect width="8192" height="8192" fill="url(#grid)" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 561 B |
4
client/web/workflow/public/icons/issue.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
|
||||
<!-- Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) -->
|
||||
<path fill="#E54122" d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 736 B |
5
client/web/workflow/public/icons/iterator.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform:rotate(360deg);-webkit-transform:rotate(360deg);transform:rotate(360deg)" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2000 2000">
|
||||
<path d="M429.65 210C214.934 210 40 384.196 40 598.5v803c0 214.304 174.934 388.5 389.65 388.5h1140.7c214.716 0 389.65-174.196 389.65-388.5v-803c0-214.304-174.934-388.5-389.65-388.5H429.65zm0 120h1140.7C1720.887 330 1840 448.826 1840 598.5v803c0 149.674-119.113 268.5-269.65 268.5H429.65C279.113 1670 160 1551.174 160 1401.5v-803C160 448.826 279.113 330 429.65 330z" fill="#0B344E"/>
|
||||
<path d="M1057.07 410.836C805.11 407.3 563.447 583.065 491.134 824.983c-55.584 173.977-23.105 373.061 85.522 520.027l-269.086-52.09l-22.804 117.814l483.865 93.664l93.687-479.986l-117.779-22.988l-56.861 291.316c-138.733-165.6-136.73-427.773 4.367-591.379c137.417-171.716 399.203-221.007 590.733-114c183.232 94.568 284.888 318.597 234.896 518.746c-44.77 208.91-247.404 367.34-460.604 363.053c-55.485-3.935-83.374 76.196-37.436 107.561c40.104 24.986 90.846 7.364 134.808 4.475c248.181-37.748 457.52-249.452 489.52-498.91c36.994-238.025-91.384-488.935-304.803-600.241c-86.168-46.769-184.068-71.364-282.089-71.21z" fill="#0B344E"/>
|
||||
<rect x="0" y="0" width="2000" height="2000" fill="rgba(0, 0, 0, 0)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
1
client/web/workflow/public/icons/play.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path fill="#43AA8B" d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"></path></svg>
|
||||
|
After Width: | Height: | Size: 392 B |
1
client/web/workflow/public/icons/prompt.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2000 2000"><g transform="translate(0 947.638)"><rect width="1800" height="1460" x="100" y="-677.638" rx="329.651" ry="328.5" fill="transparent" stroke="#0B344E" stroke-width="120" stroke-linecap="round"/><path d="M782.99 94.728h320.662c31.894 0 46.426-18.387 52.19-46.737c8.96-43.893-13.184-85.435-52.814-85.533c-97.378-.489-320.04-.196-320.04-.196v.223s340.368.268 379.564 0c39.195-.27 53.287-33.884 53.405-71.521c.12-37.54-18.907-70.744-53.287-70.793c-117.153-.098-379.68-.635-379.68-.635v-.194s199.412.465 285.185.465c35.051-.098 53.445-30.314 53.327-68.293c-.237-37.197-19.42-63.45-53.722-63.45c-137.086-.244-428.878 1.895-437.468-.146c-2.274-.54-4.22-4.971-2.723-6.794c14.054-17.122 80.562-79.478 95.325-98.59c18.512-23.803 20.486-56.943 6.394-78.401c-16.341-24.977-38.406-24.44-58.418-11.536c-57.669 37.344-260.516 178.067-305.237 208.763c-35.525 24.39-59.84 61.245-74.287 107.045c-14.604 46.435-13.105 100.51-12.947 147.875c.158 35.095.949 63.478 9.554 98.573c24.827 102.01 90.39 154.164 186.464 154.457c182.282.684 364.643.929 546.964 0c29.801-.195 46.221-19.702 46.655-55.482c.474-36.854-16.696-58.591-46.695-58.787c-70.181-.489-238.37 0-238.37 0z" fill="none" stroke="#0B344E" stroke-width="43.663900000000005" stroke-linecap="round"/></g><rect x="0" y="0" width="2000" height="2000" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
client/web/workflow/public/icons/stop.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path fill="#E54122" d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z"/></svg>
|
||||
|
After Width: | Height: | Size: 337 B |
1
client/web/workflow/public/icons/swimlane.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M96 402v1280h1856V402zm1756 97.7v1091.542H480V499.702l1372-.003zM192 499.7h192v1091.541H192z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 467 B |
1
client/web/workflow/public/icons/termination.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" width="32px" height="32px" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048"><path d="M1005.303 98.988c-447.59.377-859.063 373.342-901.291 819.483c-45.314 379.987 168.535 777.154 517.567 938.558c324.137 158.423 740.176 108.288 1009.01-135.647c273.526-236.69 393.041-640.13 272.893-984.32c-114.258-351.15-451.46-619.981-822.842-636.252c-25.079-1.64-50.213-1.947-75.337-1.822zm18.334 289.733c329.628-9.588 631.825 281.137 635.693 610.898c14.255 301.673-212.385 597.907-513.018 648.484c-290.913 58.275-613.563-109.854-715.39-392.282c-109.622-279.29 2.217-632.478 269.045-779.065c97.56-57.284 210.508-88.176 323.67-88.035zM1024 554.17c-304.682-11.568-549.567 321.3-448.846 608.674c79.046 294.298 469.266 430.4 714.132 248.923c257.896-162.49 272.52-575.472 26.765-755.798c-82.202-65.575-186.913-102.06-292.051-101.8z" fill="#0B344E"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
1
client/web/workflow/public/icons/trigger.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" preserveAspectRatio="xMidYMid meet" viewBox="0 0 2048 2048" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg);"><path d="M1899 1023.999c0-483.252-391.75-874.995-875-874.995S149 540.747 149 1024c0 483.251 391.75 875.004 875 875.004s875-391.753 875-875.004z" fill="none" stroke="#0B344E" stroke-width="100.04248826"/><rect x="0" y="0" width="2048" height="2048" fill="rgba(0, 0, 0, 0)" /></svg>
|
||||
|
After Width: | Height: | Size: 551 B |
44
client/web/workflow/public/index.html
Normal file
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-color-mode="light" lang="en">
|
||||
<head>
|
||||
<!--
|
||||
base location is root by default for all webapps
|
||||
|
||||
href value should be modified when app is placed under subdir
|
||||
-->
|
||||
<base href="/" />
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" id="favicon" href="">
|
||||
<link rel="stylesheet" type="text/css" href="<%= BASE_URL %>custom.css">
|
||||
<link rel="preload" href="<%= BASE_URL %>config.js" as="script" />
|
||||
<title><%= FLAVOUR %></title>
|
||||
<style type="text/css">
|
||||
html {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body id="workflow">
|
||||
<noscript>
|
||||
<strong>
|
||||
JavaScript is disabled in your browser!
|
||||
</strong>
|
||||
<p>
|
||||
We're sorry but Corteza doesn't work properly without JavaScript enabled.
|
||||
Please enable it to continue.
|
||||
</p>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
<script src="<%= BASE_URL %>config.js" type="text/javascript"></script>
|
||||
|
||||
<!-- contains cdns that will be injected at the head of the page -->
|
||||
<script src="<%= BASE_URL %>code-snippets.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
148
client/web/workflow/src/app.js
Normal file
@@ -0,0 +1,148 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
import './config-check'
|
||||
import './console-splash'
|
||||
|
||||
import './filters'
|
||||
import './plugins'
|
||||
import './mixins'
|
||||
import './components'
|
||||
import store from './store'
|
||||
import router from './router'
|
||||
|
||||
import { i18n, websocket } from '@cortezaproject/corteza-vue'
|
||||
|
||||
export default (options = {}) => {
|
||||
options = {
|
||||
el: '#app',
|
||||
name: 'workflow',
|
||||
template: '<router-view v-if="loaded && i18nLoaded" />',
|
||||
|
||||
data: () => ({
|
||||
loaded: false,
|
||||
i18nLoaded: false,
|
||||
}),
|
||||
|
||||
async created () {
|
||||
this.$i18n.i18next.on('initialized', () => {
|
||||
this.i18nLoaded = true
|
||||
})
|
||||
|
||||
this.websocket()
|
||||
|
||||
return this.$auth.vue(this).handle().then(async ({ user }) => {
|
||||
// switch the favicon based on the settings
|
||||
await this.$Settings.init({ api: this.$SystemAPI }).then(() => {
|
||||
const icon = this.$Settings.attachment('ui.iconLogo') || '/icon.svg'
|
||||
|
||||
const favicon = document.getElementById('favicon')
|
||||
|
||||
if (favicon) {
|
||||
favicon.href = icon
|
||||
}
|
||||
})
|
||||
|
||||
if (user.meta.preferredLanguage) {
|
||||
// After user is authenticated, get his preferred language
|
||||
// and instruct i18next to change it
|
||||
this.$i18n.i18next.changeLanguage(user.meta.preferredLanguage)
|
||||
}
|
||||
|
||||
// switch the webapp theme based on user preference
|
||||
// disabled for now as it's not working properly
|
||||
if (user.meta.theme) {
|
||||
document.getElementsByTagName('html')[0].setAttribute('data-color-mode', user.meta.theme)
|
||||
}
|
||||
|
||||
// Load effective permissions
|
||||
this.$store.dispatch('rbac/load')
|
||||
|
||||
// Initialize notifications
|
||||
this.$store.dispatch('notifications/fetchNotifications')
|
||||
|
||||
this.loaded = true
|
||||
}).catch((err) => {
|
||||
if (err instanceof Error && err.message === 'Unauthenticated') {
|
||||
// user not logged-in,
|
||||
// start with authentication flow
|
||||
this.$auth.startAuthenticationFlow()
|
||||
return
|
||||
}
|
||||
|
||||
throw err
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Registers event listener for websocket messages and
|
||||
* routes them depending on their type
|
||||
*/
|
||||
websocket () {
|
||||
// cross-link auth & websocket so that ws can use the right access token
|
||||
websocket.init(this)
|
||||
|
||||
// register event listener for messages
|
||||
this.$on('websocket-message', ({ data }) => {
|
||||
const msg = JSON.parse(data)
|
||||
switch (msg['@type']) {
|
||||
case 'notification':
|
||||
this.$store.dispatch('notifications/addNotification', msg['@value'])
|
||||
break
|
||||
|
||||
case 'notification.read':
|
||||
this.$store.dispatch('notifications/updateReadNotification', msg['@value'])
|
||||
break
|
||||
|
||||
case 'notification.unread':
|
||||
this.$store.dispatch('notifications/updateUnreadNotification', msg['@value'])
|
||||
break
|
||||
|
||||
case 'notification.read.all':
|
||||
this.$store.dispatch('notifications/updateAllReadNotifications', msg['@value'])
|
||||
break
|
||||
|
||||
case 'notification.unread.all':
|
||||
this.$store.dispatch('notifications/updateAllUnreadNotifications', msg['@value'])
|
||||
break
|
||||
|
||||
case 'notification.delete':
|
||||
this.$store.dispatch('notifications/removeNotification', msg['@value'])
|
||||
break
|
||||
|
||||
case 'error':
|
||||
this.toastDanger('Websocket message with error', msg['@value'])
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
router,
|
||||
store,
|
||||
i18n: i18n(Vue,
|
||||
{ app: 'corteza-webapp-workflow' },
|
||||
'configurator',
|
||||
'editor',
|
||||
'help',
|
||||
'general',
|
||||
'navigation',
|
||||
'notification',
|
||||
'notifications',
|
||||
'permissions',
|
||||
'configurator',
|
||||
'steps',
|
||||
),
|
||||
|
||||
// Any additional options we want to merge
|
||||
...options,
|
||||
}
|
||||
|
||||
const app = new Vue(options)
|
||||
|
||||
// Simple HMR acceptance
|
||||
if (module.hot) {
|
||||
module.hot.accept()
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
5
client/web/workflow/src/components/C3.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { default as WorkflowEditor } from './WorkflowEditor.c3'
|
||||
|
||||
export default {
|
||||
WorkflowEditor,
|
||||
}
|
||||
37
client/web/workflow/src/components/Configurator/Content.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<c-rich-text-input
|
||||
v-model="label"
|
||||
:labels="{
|
||||
urlPlaceholder: $t('steps:content.configurator.urlPlaceholder'),
|
||||
ok: $t('steps:content.configurator.ok'),
|
||||
}"
|
||||
class="m-3"
|
||||
@input="$emit('update-value', $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
const { CRichTextInput } = components
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CRichTextInput,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
|
||||
computed: {
|
||||
label: {
|
||||
get () {
|
||||
return this.item.node.value
|
||||
},
|
||||
|
||||
set (label) {
|
||||
this.item.node.value = label
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
85
client/web/workflow/src/components/Configurator/Delay.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-card
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="p-0 mb-3"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('configurator:configuration') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
<b-card-body
|
||||
class="p-0"
|
||||
>
|
||||
<b-form-group
|
||||
:label="$t('configurator:delay.duration.label')"
|
||||
:description="$t('configurator:delay.duration.description')"
|
||||
label-class="text-primary"
|
||||
class="mb-0"
|
||||
>
|
||||
<expression-editor
|
||||
v-model="item.config.arguments[0].expr"
|
||||
font-size="18px"
|
||||
show-line-numbers
|
||||
:show-popout="false"
|
||||
@input="valueChanged"
|
||||
/>
|
||||
</b-form-group>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
import ExpressionEditor from '../ExpressionEditor'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ExpressionEditor,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
|
||||
watch: {
|
||||
'item.config.stepID': {
|
||||
immediate: true,
|
||||
handler () {
|
||||
let args = [{
|
||||
target: 'offset',
|
||||
type: 'Duration',
|
||||
expr: '',
|
||||
}]
|
||||
|
||||
if (this.item.config.arguments && this.item.config.arguments.length) {
|
||||
args = this.item.config.arguments.map(({ target, type, value, expr }) => {
|
||||
return {
|
||||
target,
|
||||
type,
|
||||
expr: expr || (value ? `"${value}"` : ''),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.$set(this.item.config, 'arguments', args)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
valueChanged (value) {
|
||||
this.$emit('update-default-value', {
|
||||
value: `Delay workflow execution for ${value}`,
|
||||
force: !this.item.node.value,
|
||||
})
|
||||
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
13
client/web/workflow/src/components/Configurator/Edge.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ $t('configurator:edge') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
|
||||
export default {
|
||||
extends: base,
|
||||
}
|
||||
</script>
|
||||
124
client/web/workflow/src/components/Configurator/Error.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<b-card
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="p-0 mb-3"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('configurator:configuration') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
<b-card-body
|
||||
class="p-0"
|
||||
>
|
||||
<b-form-group
|
||||
:label="$t('general:error-expression')"
|
||||
label-class="text-primary"
|
||||
class="mb-0"
|
||||
>
|
||||
<expression-editor
|
||||
v-model="item.config.arguments[0].expr"
|
||||
font-size="18px"
|
||||
show-line-numbers
|
||||
@open="openInEditor"
|
||||
@input="valueChanged"
|
||||
/>
|
||||
</b-form-group>
|
||||
</b-card-body>
|
||||
|
||||
<b-modal
|
||||
id="expression-editor"
|
||||
:visible="!!expressionEditor.currentExpression"
|
||||
:title="$t('editor:editor')"
|
||||
size="xl"
|
||||
scrollable
|
||||
:ok-title="$t('general:save')"
|
||||
:cancel-title="$t('general:cancel')"
|
||||
cancel-variant="light"
|
||||
body-class="p-0"
|
||||
no-fade
|
||||
@ok="saveExpression"
|
||||
@hidden="resetExpression"
|
||||
>
|
||||
<expression-editor
|
||||
v-model="expressionEditor.currentExpression"
|
||||
min-height="80vh"
|
||||
font-size="18px"
|
||||
show-line-numbers
|
||||
:border="false"
|
||||
:show-popout="false"
|
||||
/>
|
||||
</b-modal>
|
||||
</b-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
import ExpressionEditor from '../ExpressionEditor.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ExpressionEditor,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
|
||||
data () {
|
||||
return {
|
||||
expressionEditor: {
|
||||
currentExpression: undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
let args = [{
|
||||
target: 'message',
|
||||
type: 'String',
|
||||
expr: '',
|
||||
}]
|
||||
|
||||
if (this.item.config.arguments && this.item.config.arguments.length) {
|
||||
args = this.item.config.arguments.map(({ target, type, value, expr }) => {
|
||||
return {
|
||||
target,
|
||||
type,
|
||||
expr: expr || (value ? `"${value}"` : ''),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
this.$set(this.item.config, 'arguments', args)
|
||||
},
|
||||
|
||||
methods: {
|
||||
valueChanged (value) {
|
||||
this.$emit('update-default-value', {
|
||||
value: `Stop workflow with error: ${value}`,
|
||||
force: !this.item.node.value,
|
||||
})
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
openInEditor () {
|
||||
this.expressionEditor.currentExpression = this.item.config.arguments[0].expr
|
||||
},
|
||||
|
||||
saveExpression () {
|
||||
const { currentExpression } = this.expressionEditor
|
||||
this.$set(this.item.config.arguments[0], 'expr', currentExpression)
|
||||
this.$root.$emit('change-detected')
|
||||
|
||||
this.resetExpression()
|
||||
},
|
||||
|
||||
resetExpression () {
|
||||
this.expressionEditor.currentExpression = undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,49 @@
|
||||
<script>
|
||||
import Function from './Function'
|
||||
|
||||
export default {
|
||||
extends: Function,
|
||||
|
||||
data () {
|
||||
return {
|
||||
showFunctionList: false,
|
||||
functionRef: 'error-handler',
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async getFunctionTypes () {
|
||||
this.functions = [
|
||||
{
|
||||
ref: 'error-handler',
|
||||
kind: 'function',
|
||||
meta: {
|
||||
short: 'Handle error',
|
||||
},
|
||||
parameters: [],
|
||||
results: [
|
||||
{
|
||||
name: 'error',
|
||||
types: [
|
||||
'Any',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'errorMessage',
|
||||
types: [
|
||||
'String',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'errorStepID',
|
||||
types: [
|
||||
'Integer',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,75 @@
|
||||
<script>
|
||||
import Function from './Function'
|
||||
|
||||
export default {
|
||||
extends: Function,
|
||||
|
||||
data () {
|
||||
return {
|
||||
showFunctionList: false,
|
||||
expressionResults: true,
|
||||
functionRef: 'exec-workflow',
|
||||
|
||||
workflowOptions: [],
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
if (!this.workflowOptions.length) {
|
||||
this.searchWorkflows('', () => {})
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async getFunctionTypes () {
|
||||
this.functions = [
|
||||
{
|
||||
ref: 'exec-workflow',
|
||||
kind: 'function',
|
||||
meta: {
|
||||
short: 'Execute a workflow',
|
||||
},
|
||||
parameters: [
|
||||
{
|
||||
name: 'workflow',
|
||||
types: [
|
||||
'ID',
|
||||
'Handle',
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
name: 'scope',
|
||||
types: [
|
||||
'Vars',
|
||||
],
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
results: [],
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
searchWorkflows (query = '', loading) {
|
||||
loading(true)
|
||||
|
||||
this.$AutomationAPI.workflowList({ query, subWorkflow: 2 })
|
||||
.then(({ set }) => {
|
||||
this.workflowOptions = set.map(m => Object.freeze(m))
|
||||
})
|
||||
.finally(() => {
|
||||
loading(false)
|
||||
})
|
||||
},
|
||||
|
||||
getWorkflowLabel ({ workflowID, handle, meta = {} }) {
|
||||
return meta.name || handle || workflowID
|
||||
},
|
||||
|
||||
getWorkflowKey ({ workflowID, handle }) {
|
||||
return handle || workflowID
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
216
client/web/workflow/src/components/Configurator/Expressions.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-card
|
||||
class="flex-grow-1 rounded-0"
|
||||
body-class="p-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="d-flex align-items-center py-4"
|
||||
>
|
||||
<h5
|
||||
class="d-flex align-items-center mb-0"
|
||||
>
|
||||
{{ $t('steps:expressions.label') }}
|
||||
<a
|
||||
:href="documentationURL"
|
||||
target="_blank"
|
||||
class="d-flex align-items-center h6 mb-0 ml-1"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="['far', 'question-circle']"
|
||||
/>
|
||||
</a>
|
||||
</h5>
|
||||
|
||||
<portal to="sidebar-footer">
|
||||
<b-button
|
||||
variant="primary"
|
||||
class="align-top border-0 ml-auto"
|
||||
@click="addArgument()"
|
||||
>
|
||||
{{ $t('steps:expressions.configurator.add-expression') }}
|
||||
</b-button>
|
||||
</portal>
|
||||
</b-card-header>
|
||||
|
||||
<b-card-body
|
||||
v-if="hasArguments"
|
||||
class="p-0"
|
||||
>
|
||||
<expression-table
|
||||
value-field="expr"
|
||||
:items="item.config.arguments"
|
||||
:fields="argumentFields"
|
||||
:types="fieldTypes"
|
||||
@remove="removeArgument"
|
||||
@open-editor="openInEditor"
|
||||
/>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<b-modal
|
||||
id="expression-editor"
|
||||
:visible="!!expressionEditor.currentExpression"
|
||||
:title="$t('editor:editor')"
|
||||
size="xl"
|
||||
scrollable
|
||||
:ok-title="$t('general:save')"
|
||||
:cancel-title="$t('general:cancel')"
|
||||
cancel-variant="light"
|
||||
body-class="p-0"
|
||||
no-fade
|
||||
@ok="saveExpression"
|
||||
@hidden="resetExpression"
|
||||
>
|
||||
<expression-editor
|
||||
v-model="currentExpressionValue"
|
||||
min-height="80vh"
|
||||
font-size="18px"
|
||||
show-line-numbers
|
||||
:border="false"
|
||||
:show-popout="false"
|
||||
/>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
import ExpressionTable from '../ExpressionTable.vue'
|
||||
import ExpressionEditor from '../ExpressionEditor.vue'
|
||||
import { getDocumentationURL } from '../../lib/version'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ExpressionEditor,
|
||||
ExpressionTable,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
|
||||
data () {
|
||||
return {
|
||||
fieldTypes: [],
|
||||
|
||||
expressionEditor: {
|
||||
currentIndex: undefined,
|
||||
currentExpression: undefined,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
currentExpressionValue: {
|
||||
get () {
|
||||
return this.expressionEditor.currentExpression ? this.expressionEditor.currentExpression.expr : ''
|
||||
},
|
||||
|
||||
set (value) {
|
||||
if (this.expressionEditor.currentExpression) {
|
||||
this.expressionEditor.currentExpression.expr = value
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
argumentFields () {
|
||||
return [
|
||||
{
|
||||
key: 'target',
|
||||
label: this.$t('steps:expressions.configurator.target'),
|
||||
thClass: 'pl-4 ml-1',
|
||||
formatter: (item) => {
|
||||
return `${item.target}(${item.type})`
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'expr',
|
||||
label: this.$t('steps:expressions.configurator.expression'),
|
||||
thClass: 'pl-1 mr-3',
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
hasArguments () {
|
||||
const { config } = this.item || {}
|
||||
return (config && (config.arguments || []).length) || []
|
||||
},
|
||||
|
||||
documentationURL () {
|
||||
return getDocumentationURL('integrator-guide/expr/index.html')
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
'item.config.stepID': {
|
||||
immediate: true,
|
||||
handler () {
|
||||
this.$set(this.item.config, 'arguments', this.item.config.arguments || [])
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
created () {
|
||||
this.getTypes()
|
||||
},
|
||||
|
||||
methods: {
|
||||
addArgument () {
|
||||
this.item.config.arguments.push({
|
||||
target: '',
|
||||
expr: '',
|
||||
type: 'Any',
|
||||
_showDetails: true,
|
||||
})
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
removeArgument (index) {
|
||||
this.item.config.arguments.splice(index, 1)
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
openInEditor (index = -1) {
|
||||
this.expressionEditor = {
|
||||
currentIndex: index >= -1 ? index : undefined,
|
||||
currentExpression: index >= 0 ? { ...this.item.config.arguments[index] } : undefined,
|
||||
}
|
||||
},
|
||||
|
||||
saveExpression () {
|
||||
if (this.expressionEditor.currentIndex >= 0) {
|
||||
const args = [...this.item.config.arguments]
|
||||
args[this.expressionEditor.currentIndex] = this.expressionEditor.currentExpression
|
||||
this.$set(this.item.config, 'arguments', args)
|
||||
this.$root.$emit('change-detected')
|
||||
}
|
||||
|
||||
this.resetExpression()
|
||||
},
|
||||
|
||||
resetExpression () {
|
||||
this.expressionEditor = {
|
||||
currentIndex: undefined,
|
||||
currentExpression: undefined,
|
||||
}
|
||||
},
|
||||
|
||||
async getTypes () {
|
||||
return this.$AutomationAPI.typeList()
|
||||
.then(({ set }) => {
|
||||
this.fieldTypes = set
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:fetch-types-failed')))
|
||||
},
|
||||
|
||||
getTypeDescription (type) {
|
||||
// This will be moved to backend field type information
|
||||
const typeDescriptions = {
|
||||
ID: 'Make sure to provide the ID in double quotes if you\'re using a literal value. Example "123"',
|
||||
}
|
||||
|
||||
return typeDescriptions[type]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
709
client/web/workflow/src/components/Configurator/Function.vue
Normal file
@@ -0,0 +1,709 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="!processing"
|
||||
>
|
||||
<b-card
|
||||
v-if="showFunctionList"
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="p-0 mb-3"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('configurator:configuration') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
<b-card-body
|
||||
v-if="functionTypes.length"
|
||||
class="p-0"
|
||||
>
|
||||
<b-form-group
|
||||
:label="$t('steps:function.configurator.type*')"
|
||||
label-class="text-primary"
|
||||
class="mb-0"
|
||||
>
|
||||
<c-input-select
|
||||
v-model="functionRef"
|
||||
:options="functionTypes"
|
||||
:get-option-key="getOptionTypeKey"
|
||||
label="text"
|
||||
:selectable="f => !f.disabled"
|
||||
:reduce="f => f.value"
|
||||
:filter="functionFilter"
|
||||
:placeholder="$t('steps:function.configurator.select-function')"
|
||||
@input="functionChanged"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<p
|
||||
v-if="functionDescription"
|
||||
class="mt-3 mb-0"
|
||||
>
|
||||
{{ functionDescription }}
|
||||
</p>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<b-card
|
||||
v-if="args.length"
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
body-class="p-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('steps:function.configurator.arguments') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
|
||||
<b-card-body
|
||||
class="p-0"
|
||||
>
|
||||
<b-table
|
||||
id="arguments"
|
||||
fixed
|
||||
borderless
|
||||
hover
|
||||
head-variant="light"
|
||||
details-td-class="bg-white"
|
||||
:items="args"
|
||||
:fields="argumentFields"
|
||||
:tbody-tr-class="rowClass"
|
||||
class="mb-4"
|
||||
@row-clicked="item=>$set(item, '_showDetails', !item._showDetails)"
|
||||
>
|
||||
<template #cell(target)="{ item: a }">
|
||||
<strong>{{ a.label || a.target }}<span
|
||||
v-if="a.required"
|
||||
class="text-danger"
|
||||
>*</span></strong>
|
||||
<samp v-if="!isWhileIterator"> ({{ a.type }})</samp>
|
||||
</template>
|
||||
|
||||
<template #cell(type)="{ item: a }">
|
||||
<var>{{ a.type }}</var>
|
||||
</template>
|
||||
|
||||
<template #cell(value)="{ item: a }">
|
||||
<samp>{{ a[a.valueType] }}</samp>
|
||||
</template>
|
||||
|
||||
<template #cell(expression)="{ item: a }">
|
||||
<span
|
||||
v-if="a.valueType === 'expr'"
|
||||
v-b-tooltip
|
||||
:title="$t('steps:function.configurator.expression')"
|
||||
class="circle-badge"
|
||||
>
|
||||
e
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template #row-details="{ item: a, index }">
|
||||
<div class="arrow-up" />
|
||||
|
||||
<b-card
|
||||
class="bg-light"
|
||||
body-class="px-4 pb-3"
|
||||
>
|
||||
<b-form-group
|
||||
v-if="(paramTypes[functionRef][a.target] || []).length > 1"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<c-input-select
|
||||
v-model="a.type"
|
||||
:options="(paramTypes[functionRef][a.target] || [])"
|
||||
:get-option-key="getOptionParamKey"
|
||||
:filter="argTypeFilter"
|
||||
:clearable="false"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
label-class="d-flex align-items-center text-primary"
|
||||
class="mb-0"
|
||||
>
|
||||
<div
|
||||
v-if="a.valueType === 'value'"
|
||||
>
|
||||
<c-input-select
|
||||
v-if="a.target === 'workflow'"
|
||||
v-model="a.value"
|
||||
:options="workflowOptions"
|
||||
:get-option-label="getWorkflowLabel"
|
||||
:get-option-key="getWorkflowKey"
|
||||
:reduce="wf => a.type === 'ID' ? wf.workflowID : wf.handle"
|
||||
:placeholder="$t('steps:function.configurator.search-workflow')"
|
||||
:filterable="false"
|
||||
@input="$root.$emit('change-detected')"
|
||||
@search="searchWorkflows"
|
||||
/>
|
||||
|
||||
<c-input-select
|
||||
v-else-if="a.input.type === 'select'"
|
||||
v-model="a.value"
|
||||
:options="a.input.properties.options"
|
||||
:get-option-key="getOptionTypeKey"
|
||||
label="text"
|
||||
:filter="varFilter"
|
||||
:reduce="a => a.value"
|
||||
:placeholder="$t('steps:function.configurator.option-select')"
|
||||
:clearable="false"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
|
||||
<c-rich-text-input
|
||||
v-else-if="a.input.type === 'richtext'"
|
||||
v-model="a.value"
|
||||
:labels="{
|
||||
urlPlaceholder: $t('steps:function.configurator.urlPlaceholder'),
|
||||
ok: $t('steps:function.configurator.ok'),
|
||||
}"
|
||||
min-body-height="6rem"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
|
||||
<b-form-checkbox
|
||||
v-else-if="a.type === 'Boolean'"
|
||||
v-model="a.value"
|
||||
value="true"
|
||||
size="md"
|
||||
unchecked-value="false"
|
||||
@input="$root.$emit('change-detected')"
|
||||
>
|
||||
{{ a.target }}
|
||||
</b-form-checkbox>
|
||||
|
||||
<expression-editor
|
||||
v-else
|
||||
v-model="a.value"
|
||||
:auto-complete="false"
|
||||
@open="openInEditor(index)"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<expression-editor
|
||||
v-else-if="a.valueType === 'expr'"
|
||||
v-model="a.expr"
|
||||
show-line-numbers
|
||||
@open="openInEditor(index)"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-checkbox
|
||||
v-if="!isWhileIterator"
|
||||
v-model="a.valueType"
|
||||
value="expr"
|
||||
unchecked-value="value"
|
||||
switch
|
||||
size="sm"
|
||||
class="float-right mr-2 mt-2"
|
||||
@change="valueTypeChanged($event, index)"
|
||||
>
|
||||
<div
|
||||
class="d-flex"
|
||||
>
|
||||
{{ $t('steps:function.configurator.expression') }}
|
||||
<a
|
||||
:href="documentationURL"
|
||||
target="_blank"
|
||||
class="d-flex align-items-center h6 mb-0 ml-1"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="['far', 'question-circle']"
|
||||
class="ml-1"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</b-form-checkbox>
|
||||
</b-card>
|
||||
</template>
|
||||
</b-table>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<b-card
|
||||
v-if="expressionResults || results.length"
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
body-class="p-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('steps:function.configurator.results') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
|
||||
<b-card-body
|
||||
v-if="results.length"
|
||||
class="p-0"
|
||||
>
|
||||
<expression-table
|
||||
v-if="expressionResults"
|
||||
value-field="expr"
|
||||
:items="results"
|
||||
:fields="resultFields"
|
||||
:types="fieldTypes"
|
||||
@remove="removeResult"
|
||||
@open-editor="openInEditor"
|
||||
/>
|
||||
|
||||
<b-table
|
||||
v-else
|
||||
id="results"
|
||||
fixed
|
||||
borderless
|
||||
hover
|
||||
head-variant="light"
|
||||
details-td-class="bg-white"
|
||||
class="mb-4"
|
||||
:items="results"
|
||||
:fields="resultFields"
|
||||
:tbody-tr-class="rowClass"
|
||||
@row-clicked="item=>$set(item, '_showDetails', !item._showDetails)"
|
||||
>
|
||||
<template #cell(type)="{ item: a }">
|
||||
<var>{{ a.type }}</var>
|
||||
</template>
|
||||
|
||||
<template #cell(value)="{ item: a }">
|
||||
<samp>{{ a.expr }}</samp>
|
||||
</template>
|
||||
|
||||
<template #row-details="{ item: a }">
|
||||
<div class="arrow-up" />
|
||||
|
||||
<b-card
|
||||
class="bg-light"
|
||||
body-class="px-4 pb-3"
|
||||
>
|
||||
<b-form-group
|
||||
class="mb-0"
|
||||
>
|
||||
<b-form-input
|
||||
v-model="a.target"
|
||||
:placeholder="$t('configurator:target')"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
</b-card>
|
||||
</template>
|
||||
</b-table>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<portal to="sidebar-footer">
|
||||
<b-button
|
||||
v-if="expressionResults"
|
||||
variant="primary"
|
||||
class="align-top border-0 ml-auto"
|
||||
@click="addResult()"
|
||||
>
|
||||
{{ $t('steps:function.configurator.add-result') }}
|
||||
</b-button>
|
||||
</portal>
|
||||
|
||||
<b-modal
|
||||
id="expression-editor"
|
||||
:visible="!!expressionEditor.currentExpression"
|
||||
:title="$t('editor:editor')"
|
||||
size="xl"
|
||||
scrollable
|
||||
:ok-title="$t('general:save')"
|
||||
:cancel-title="$t('general:cancel')"
|
||||
cancel-variant="light"
|
||||
body-class="p-0"
|
||||
no-fade
|
||||
@ok="saveExpression"
|
||||
@hidden="resetExpression"
|
||||
>
|
||||
<expression-editor
|
||||
v-model="currentExpressionValue"
|
||||
:lang="expressionEditor.lang"
|
||||
min-height="80vh"
|
||||
font-size="18px"
|
||||
show-line-numbers
|
||||
:border="false"
|
||||
:show-popout="false"
|
||||
/>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
import ExpressionTable from '../ExpressionTable.vue'
|
||||
import ExpressionEditor from '../ExpressionEditor.vue'
|
||||
import { objectSearchMaker, stringSearchMaker } from '../../lib/filter'
|
||||
import { getDocumentationURL } from '../../lib/version'
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
const { CRichTextInput } = components
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CRichTextInput,
|
||||
ExpressionEditor,
|
||||
ExpressionTable,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
|
||||
data () {
|
||||
return {
|
||||
processing: true,
|
||||
|
||||
showFunctionList: true,
|
||||
expressionResults: false,
|
||||
functionRef: undefined,
|
||||
|
||||
functions: [],
|
||||
args: [],
|
||||
results: [],
|
||||
|
||||
fieldTypes: [],
|
||||
|
||||
paramTypes: {},
|
||||
resultTypes: {},
|
||||
|
||||
expressionEditor: {
|
||||
currentIndex: undefined,
|
||||
currentExpression: undefined,
|
||||
lang: 'javascript',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
// Used for expression editor modal
|
||||
currentExpressionValue: {
|
||||
get () {
|
||||
const { currentExpression } = this.expressionEditor
|
||||
return currentExpression ? currentExpression[currentExpression.valueType] : ''
|
||||
},
|
||||
|
||||
set (value) {
|
||||
const { currentExpression } = this.expressionEditor
|
||||
|
||||
if (currentExpression) {
|
||||
currentExpression[currentExpression.valueType] = value
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
functionTypes () {
|
||||
return this.functions.map(({ ref, meta, disabled = false }) => ({ value: ref, text: meta.short, disabled }))
|
||||
},
|
||||
|
||||
argumentFields () {
|
||||
return [
|
||||
{
|
||||
key: 'target',
|
||||
label: this.$t('steps:function.configurator.name'),
|
||||
thClass: 'pl-3 py-2',
|
||||
tdClass: 'text-truncate pointer',
|
||||
},
|
||||
{
|
||||
key: 'value',
|
||||
thClass: 'pr-3 py-2',
|
||||
tdClass: 'text-truncate pointer',
|
||||
},
|
||||
{
|
||||
key: 'expression',
|
||||
label: '',
|
||||
thClass: 'text-center',
|
||||
tdClass: 'text-center pointer',
|
||||
thStyle: {
|
||||
width: '3rem',
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
resultFields () {
|
||||
return [
|
||||
{
|
||||
key: 'target',
|
||||
label: this.$t('steps:function.configurator.target'),
|
||||
thClass: 'pl-3',
|
||||
tdClass: 'text-truncate pointer',
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
label: this.$t('steps:function.configurator.type'),
|
||||
tdClass: 'text-truncate pointer',
|
||||
},
|
||||
{
|
||||
key: 'expr',
|
||||
label: this.$t('steps:function.configurator.result'),
|
||||
thClass: 'mr-3',
|
||||
tdClass: 'position-relative pointer',
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
valueTypes () {
|
||||
return [
|
||||
{ text: this.$t('steps:function.configurator.expression'), value: 'expr' },
|
||||
{ text: this.$t('steps:function.configurator.constant'), value: 'value' },
|
||||
]
|
||||
},
|
||||
|
||||
defaultOptions () {
|
||||
return [{ value: null, text: this.$t('steps:function.configurator.option-select'), disabled: true }]
|
||||
},
|
||||
|
||||
functionDescription () {
|
||||
return (this.functions.find(({ ref }) => ref === this.functionRef) || { meta: {} }).meta.description
|
||||
},
|
||||
|
||||
isWhileIterator () {
|
||||
if (this.item.config) {
|
||||
return this.item.config.kind === 'iterator' && this.functionRef === 'loopDo'
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
documentationURL () {
|
||||
return getDocumentationURL('integrator-guide/expr/index.html')
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
'item.config.stepID': {
|
||||
immediate: true,
|
||||
async handler () {
|
||||
this.processing = true
|
||||
|
||||
this.$set(this.item.config, 'arguments', this.item.config.arguments || [])
|
||||
this.$set(this.item.config, 'results', this.item.config.results || [])
|
||||
|
||||
await this.getFunctionTypes()
|
||||
await this.getTypes()
|
||||
|
||||
this.functionRef = this.item.config.ref || this.functionRef
|
||||
|
||||
this.setParams(this.functionRef, true)
|
||||
|
||||
this.processing = false
|
||||
},
|
||||
},
|
||||
|
||||
args: {
|
||||
deep: true,
|
||||
handler (args) {
|
||||
this.item.config.arguments = args.filter(({ value, source, expr }) => value || source || expr)
|
||||
.map(arg => {
|
||||
const argMapped = {
|
||||
target: arg.target,
|
||||
type: arg.type,
|
||||
}
|
||||
|
||||
argMapped[arg.valueType] = arg[arg.valueType]
|
||||
|
||||
return argMapped
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
results: {
|
||||
deep: true,
|
||||
handler (res) {
|
||||
this.item.config.results = res.filter(({ target }) => target).map(({ target, expr, type }) => ({ target, type, expr }))
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
functionFilter: objectSearchMaker('text'),
|
||||
argTypeFilter: stringSearchMaker(),
|
||||
varFilter: objectSearchMaker('text'),
|
||||
|
||||
setParams (fName, immediate = false) {
|
||||
this.args = []
|
||||
this.results = []
|
||||
|
||||
if (!immediate) {
|
||||
this.$root.$emit('change-detected')
|
||||
}
|
||||
|
||||
if (fName) {
|
||||
const func = this.functions.find(({ ref }) => ref === fName)
|
||||
|
||||
// Set parameters
|
||||
if (!this.paramTypes[func.ref] && func.parameters) {
|
||||
this.paramTypes[func.ref] = {}
|
||||
func.parameters.forEach(({ name, types }) => {
|
||||
this.paramTypes[func.ref][name] = types || []
|
||||
})
|
||||
}
|
||||
|
||||
this.args = func.parameters?.map(param => {
|
||||
const arg = this.item.config.arguments.find(({ target }) => target === param.name) || {}
|
||||
const { input = {} } = (param.meta || {}).visual || {}
|
||||
return {
|
||||
name: param.name,
|
||||
target: param.name,
|
||||
label: (param.meta || {}).label,
|
||||
type: arg.type || this.paramTypes[func.ref][param.name][0],
|
||||
valueType: arg.expr !== undefined ? 'expr' : 'value',
|
||||
value: arg.value || input.default || null,
|
||||
expr: arg.expr || arg.source || null,
|
||||
required: param.required || false,
|
||||
input,
|
||||
}
|
||||
}) || []
|
||||
|
||||
// Set results
|
||||
if (!this.expressionResults) {
|
||||
if (!this.resultTypes[func.ref] && func.results) {
|
||||
this.resultTypes[func.ref] = {}
|
||||
func.results.forEach(({ name, types }) => {
|
||||
this.resultTypes[func.ref][name] = types || []
|
||||
})
|
||||
}
|
||||
|
||||
this.results = func.results?.map(result => {
|
||||
const res = this.item.config.results.find(({ expr }) => expr === result.name) || {}
|
||||
return {
|
||||
name: result.name,
|
||||
valueType: 'expr',
|
||||
target: res.target || undefined,
|
||||
type: this.resultTypes[func.ref][result.name][0],
|
||||
expr: res.expr || result.name,
|
||||
}
|
||||
}) || []
|
||||
} else {
|
||||
this.results = this.item.config.results.map(({ target, type, expr }) => {
|
||||
return {
|
||||
valueType: 'expr',
|
||||
target,
|
||||
type,
|
||||
expr,
|
||||
}
|
||||
}) || []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
openInEditor (index = -1) {
|
||||
this.expressionEditor = {
|
||||
currentIndex: index >= -1 ? index : undefined,
|
||||
currentExpression: index >= 0 ? { ...this.args[index] } : undefined,
|
||||
}
|
||||
|
||||
this.expressionEditor.lang = this.expressionEditor.currentExpression.valueType === 'expr' ? 'javascript' : 'text'
|
||||
},
|
||||
|
||||
saveExpression () {
|
||||
const { currentIndex = -1, currentExpression } = this.expressionEditor
|
||||
if (currentIndex >= 0) {
|
||||
this.args[currentIndex] = currentExpression
|
||||
this.$set(this.args, currentIndex, currentExpression)
|
||||
this.$root.$emit('change-detected')
|
||||
}
|
||||
|
||||
this.resetExpression()
|
||||
},
|
||||
|
||||
resetExpression () {
|
||||
this.expressionEditor = {
|
||||
currentIndex: undefined,
|
||||
currentExpression: undefined,
|
||||
lang: 'javascript',
|
||||
}
|
||||
},
|
||||
|
||||
async getFunctionTypes () {
|
||||
return this.$AutomationAPI.functionList()
|
||||
.then(({ set }) => {
|
||||
this.functions = set.filter(({ kind = '' }) => kind !== 'iterator').sort((a, b) => a.meta.short.localeCompare(b.meta.short))
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:failed-fetch-functions')))
|
||||
},
|
||||
|
||||
async getTypes () {
|
||||
return this.$AutomationAPI.typeList()
|
||||
.then(({ set }) => {
|
||||
this.fieldTypes = set
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:fetch-types-failed')))
|
||||
},
|
||||
|
||||
functionChanged (functionRef) {
|
||||
this.item.config.ref = functionRef
|
||||
|
||||
this.setParams(functionRef)
|
||||
|
||||
this.$emit('update-default-value', {
|
||||
value: (this.functionTypes.find(({ value }) => value === functionRef) || { meta: {} }).text,
|
||||
force: !this.item.node.value,
|
||||
})
|
||||
},
|
||||
|
||||
valueTypeChanged (valueType, index) {
|
||||
const oldType = valueType === 'value' ? 'expr' : 'value'
|
||||
this.args[index][valueType] = this.args[index][oldType]
|
||||
|
||||
if (!this.args[index].value && this.args[index].type === 'Boolean' && valueType === 'value') {
|
||||
this.args[index].value = 'false'
|
||||
}
|
||||
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
rowClass (item, type) {
|
||||
return item._showDetails && type === 'row' ? 'border-thick' : 'border-thick-transparent'
|
||||
},
|
||||
|
||||
addResult () {
|
||||
this.results.push({
|
||||
target: '',
|
||||
expr: '',
|
||||
type: 'Any',
|
||||
_showDetails: true,
|
||||
})
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
removeResult (index) {
|
||||
this.results.splice(index, 1)
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
getTypeDescription (type) {
|
||||
// This will be moved to backend field type information
|
||||
const typeDescriptions = {
|
||||
ID: 'Make sure to provide the ID in double quotes if you\'re using a literal value. Example "123"',
|
||||
}
|
||||
|
||||
return typeDescriptions[type]
|
||||
},
|
||||
|
||||
getOptionTypeKey ({ value }) {
|
||||
return value
|
||||
},
|
||||
|
||||
getOptionEWorkflowLabelKey ({ workflowID }) {
|
||||
return workflowID
|
||||
},
|
||||
|
||||
getOptionParamKey (type) {
|
||||
return type
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
93
client/web/workflow/src/components/Configurator/Gateway.vue
Normal file
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-card
|
||||
v-if="['incl', 'excl'].includes(gatewayKind)"
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="p-0 mb-3"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('configurator:configuration') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
|
||||
<b-card-body
|
||||
class="p-0"
|
||||
>
|
||||
<var
|
||||
v-if="outEdges < 2"
|
||||
>
|
||||
{{ $t('steps:gateway.configurator.two-paths') }}
|
||||
</var>
|
||||
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
<b-form-group
|
||||
v-for="edge in gatewayEdges"
|
||||
:key="edge.id"
|
||||
:label="edge.value"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<expression-editor
|
||||
v-model="edge.expr"
|
||||
show-line-numbers
|
||||
:show-popout="false"
|
||||
@input="updateEdge(edge.id, $event)"
|
||||
/>
|
||||
</b-form-group>
|
||||
</div>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
import ExpressionEditor from '../ExpressionEditor.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ExpressionEditor,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
|
||||
computed: {
|
||||
gatewayKind () {
|
||||
return this.item.config.ref
|
||||
},
|
||||
|
||||
gatewayEdges () {
|
||||
const edges = []
|
||||
if (['incl', 'excl'].includes(this.gatewayKind)) {
|
||||
if (this.outEdges && this.item.node.edges) {
|
||||
this.item.node.edges.forEach(({ id, source, target, value = '' }) => {
|
||||
if (source.id === this.item.node.id) {
|
||||
edges.push({
|
||||
id,
|
||||
source: source.id,
|
||||
target: target.id,
|
||||
value,
|
||||
expr: this.edges[id].config.expr || '',
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return edges
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateEdge (id, expr) {
|
||||
this.edges[id].config.expr = expr
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
17
client/web/workflow/src/components/Configurator/Iterator.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<script>
|
||||
import Function from './Function'
|
||||
|
||||
export default {
|
||||
extends: Function,
|
||||
|
||||
methods: {
|
||||
async getFunctionTypes () {
|
||||
return this.$AutomationAPI.functionList()
|
||||
.then(({ set }) => {
|
||||
this.functions = set.filter(({ kind = '' }) => kind === 'iterator').sort((a, b) => a.meta.short.localeCompare(b.meta.short))
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:failed-fetch-functions')))
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
14
client/web/workflow/src/components/Configurator/Prompt.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<script>
|
||||
import Function from './Function'
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
|
||||
export default {
|
||||
extends: Function,
|
||||
|
||||
methods: {
|
||||
async getFunctionTypes () {
|
||||
this.functions = components.promptDefinitions || []
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
534
client/web/workflow/src/components/Configurator/Trigger.vue
Normal file
@@ -0,0 +1,534 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-card
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="p-0 mb-3"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('configurator:configuration') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
<b-card-body
|
||||
class="p-0"
|
||||
>
|
||||
<b-form-group
|
||||
:label="$t('steps:trigger.configurator.resource*')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<c-input-select
|
||||
v-model="item.triggers.resourceType"
|
||||
:options="resourceTypeOptions"
|
||||
:get-option-key="getOptionTypeKey"
|
||||
label="text"
|
||||
:reduce="r => r.value"
|
||||
:filter="resTypeFilter"
|
||||
:placeholder="$t('steps:trigger.configurator.select-resource-type')"
|
||||
:clearable="false"
|
||||
@input="resourceChanged"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
v-if="item.triggers.resourceType"
|
||||
:label="$t('steps:trigger.configurator.event*')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<c-input-select
|
||||
v-model="item.triggers.eventType"
|
||||
:options="eventTypeOptions"
|
||||
:get-option-key="getOptionEventTypeKey"
|
||||
:get-option-label="getEventTypeLabel"
|
||||
:reduce="e => e.eventType"
|
||||
:filter="evtTypeFilter"
|
||||
:placeholder="$t('steps:trigger.configurator.select-event-type')"
|
||||
:clearable="false"
|
||||
@input="eventChanged"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
class="mb-0"
|
||||
>
|
||||
<b-form-checkbox
|
||||
v-model="item.triggers.enabled"
|
||||
:disabled="isSubworkflow && !item.triggers.enabled"
|
||||
class="text-primary"
|
||||
@change="enabledChanged()"
|
||||
>
|
||||
{{ $t('general:enabled') }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<b-card
|
||||
v-if="showConstraints"
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
body-class="p-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('steps:trigger.configurator.constraints') }}
|
||||
</h5>
|
||||
|
||||
<b-button
|
||||
v-if="constraintNameTypes.length"
|
||||
variant="primary"
|
||||
class="ml-3"
|
||||
@click="addConstraint()"
|
||||
>
|
||||
{{ $t('steps:trigger.configurator.add-constraints') }}
|
||||
</b-button>
|
||||
</b-card-header>
|
||||
|
||||
<b-card-body
|
||||
class="p-0"
|
||||
>
|
||||
<b-table
|
||||
v-if="constraintNameTypes.length"
|
||||
id="constraints"
|
||||
fixed
|
||||
borderless
|
||||
hover
|
||||
head-variant="light"
|
||||
details-td-class="bg-white"
|
||||
:items="item.triggers.constraints"
|
||||
:fields="constraintFields"
|
||||
:tbody-tr-class="rowClass"
|
||||
show-empty
|
||||
class="mb-0"
|
||||
@row-clicked="item=>$set(item, '_showDetails', !item._showDetails)"
|
||||
>
|
||||
<template #empty>
|
||||
<p class="text-center text-muted m-4">
|
||||
{{ $t('steps:trigger.configurator.no-constraints') }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template #cell(name)="{ item: c }">
|
||||
{{ getConstraintNameLabel(c.name) }}
|
||||
</template>
|
||||
|
||||
<template #cell(op)="{ item: c }">
|
||||
<p style="margin-bottom: 0.4rem;">
|
||||
{{ getConstraintOperatorLabel(c.op) }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<template #cell(values)="{ item: c, index }">
|
||||
{{ c.values.join(' or ') }}
|
||||
|
||||
<c-input-confirm
|
||||
show-icon
|
||||
class="delete-btn ml-auto"
|
||||
@confirmed="removeConstraint(index)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #row-details="{ item: c }">
|
||||
<div class="arrow-up" />
|
||||
|
||||
<b-card class="bg-light">
|
||||
<b-form-group
|
||||
:label="$t('steps:trigger.configurator.resource')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<c-input-select
|
||||
v-model="c.name"
|
||||
:options="constraintNameTypes"
|
||||
:get-option-key="getOptionTypeKey"
|
||||
label="text"
|
||||
:reduce="c => c.value"
|
||||
:filter="constrFilter"
|
||||
:placeholder="$t('steps:trigger.configurator.select-constraint-type')"
|
||||
:clearable="false"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
:label="$t('steps:trigger.configurator.operator')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<c-input-select
|
||||
v-model="c.op"
|
||||
:options="constraintOperatorTypes"
|
||||
:get-option-key="getOptionTypeKey"
|
||||
label="text"
|
||||
:reduce="c => c.value"
|
||||
:placeholder="$t('steps:trigger.configurator.select-operator')"
|
||||
:clearable="false"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
label="Values"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<div
|
||||
v-for="(value, index) in c.values"
|
||||
:key="index"
|
||||
class="mb-2"
|
||||
>
|
||||
<p
|
||||
v-if="index > 0"
|
||||
class="text-center text-uppercase text-muted mb-2"
|
||||
>
|
||||
{{ $t('general:label.or') }}
|
||||
</p>
|
||||
|
||||
<div class="d-flex align-items-center gap-1">
|
||||
<b-form-input
|
||||
v-model="c.values[index]"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
|
||||
<c-input-confirm
|
||||
show-icon
|
||||
@confirmed="c.values.splice(index, 1)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<b-button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
class="mr-auto"
|
||||
@click="c.values.push('')"
|
||||
>
|
||||
{{ $t('steps:trigger.configurator.add') }}
|
||||
</b-button>
|
||||
</b-form-group>
|
||||
</b-card>
|
||||
</template>
|
||||
</b-table>
|
||||
|
||||
<b-form-group
|
||||
v-else-if="item.triggers.constraints[0]"
|
||||
label-class="d-flex align-items-center text-primary"
|
||||
class="mt-0 mb-4 mx-4"
|
||||
>
|
||||
<template #label>
|
||||
{{ item.triggers.eventType.replace('on', '') }}
|
||||
<a
|
||||
:href="intervalDocumentationURL"
|
||||
target="_blank"
|
||||
class="d-flex align-items-center h6 mb-0 ml-1 pointer"
|
||||
>
|
||||
<font-awesome-icon
|
||||
:icon="['far', 'question-circle']"
|
||||
/>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<c-input-date-time
|
||||
v-if="item.triggers.eventType === 'onTimestamp'"
|
||||
v-model="item.triggers.constraints[0].values[0]"
|
||||
:labels="{
|
||||
clear: $t('general:clear'),
|
||||
none: $t('general:none'),
|
||||
now: $t('general:now'),
|
||||
today: $t('general:today'),
|
||||
}"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
|
||||
<b-form-input
|
||||
v-else
|
||||
v-model="item.triggers.constraints[0].values[0]"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<b-card
|
||||
v-if="(eventType.properties || []).length"
|
||||
class="flex-grow-1 rounded-0"
|
||||
body-class="p-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('steps:trigger.configurator.initial-scope') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
<b-card-body
|
||||
class="p-0"
|
||||
>
|
||||
<b-table
|
||||
id="variable"
|
||||
fixed
|
||||
borderless
|
||||
head-variant="light"
|
||||
class="mb-4"
|
||||
:items="eventType.properties || []"
|
||||
:fields="scopeFields"
|
||||
>
|
||||
<template #cell(type)="{ item: v }">
|
||||
<var>{{ v.type || $t('general:label.any') }}</var>
|
||||
</template>
|
||||
</b-table>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
import { objectSearchMaker } from '../../lib/filter'
|
||||
import { getConstraintNameLabel } from '../../lib/constraint'
|
||||
import { getDocumentationURL } from '../../lib/version'
|
||||
import { camelToTitle } from '../../lib/string'
|
||||
const { CInputDateTime } = components
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CInputDateTime,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
|
||||
data () {
|
||||
return {
|
||||
modules: [],
|
||||
|
||||
eventTypes: [],
|
||||
resourceTypes: [],
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
resourceTypeOptions () {
|
||||
return this.resourceTypes
|
||||
},
|
||||
|
||||
eventTypeOptions () {
|
||||
return this.eventTypes.filter(({ resourceType }) => resourceType === this.item.triggers.resourceType)
|
||||
},
|
||||
|
||||
eventType () {
|
||||
return this.eventTypes.find(({ resourceType, eventType }) => resourceType === this.item.triggers.resourceType && eventType === this.item.triggers.eventType) || {}
|
||||
},
|
||||
|
||||
showConstraints () {
|
||||
if (this.item.triggers.resourceType && this.item.triggers.eventType) {
|
||||
return this.constraintNameTypes.length ? true : this.item.triggers.eventType !== 'onManual'
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
constraintFields () {
|
||||
return [
|
||||
{
|
||||
key: 'name',
|
||||
tdClass: 'pointer',
|
||||
},
|
||||
{
|
||||
key: 'op',
|
||||
label: this.$t('steps:trigger.configurator.operator'),
|
||||
thClass: 'text-center',
|
||||
tdClass: 'text-truncate text-center pointer',
|
||||
},
|
||||
{
|
||||
key: 'values',
|
||||
tdClass: 'd-flex align-items-start gap-2 pointer pr-2',
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
scopeFields () {
|
||||
return [
|
||||
{
|
||||
key: 'name',
|
||||
thClass: 'pl-3 py-2',
|
||||
tdClass: 'text-truncate',
|
||||
},
|
||||
{
|
||||
key: 'type',
|
||||
thClass: 'pr-3 py-2',
|
||||
tdClass: 'text-truncate',
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
constraintNameTypes () {
|
||||
const constraints = this.eventType.constraints || []
|
||||
|
||||
return constraints.reduce((cons, { name }) => {
|
||||
if (!name.includes('*')) {
|
||||
cons.push({
|
||||
value: name,
|
||||
text: this.getConstraintNameLabel(name),
|
||||
})
|
||||
}
|
||||
|
||||
return cons
|
||||
}, [])
|
||||
},
|
||||
|
||||
constraintOperatorTypes () {
|
||||
return [
|
||||
{ value: '=', text: this.$t('steps:trigger.configurator.equal') },
|
||||
{ value: '!=', text: this.$t('steps:trigger.configurator.not-equal') },
|
||||
{ value: 'like', text: this.$t('steps:trigger.configurator.like') },
|
||||
{ value: 'not like', text: this.$t('steps:trigger.configurator.not-like') },
|
||||
]
|
||||
},
|
||||
|
||||
intervalDocumentationURL () {
|
||||
return getDocumentationURL('integrator-guide/automation/workflows/index.html#deferred-interval')
|
||||
},
|
||||
},
|
||||
|
||||
async created () {
|
||||
if (!this.item.triggers) {
|
||||
this.$set(this.item, 'triggers', {
|
||||
resourceType: null,
|
||||
eventType: null,
|
||||
constraints: [],
|
||||
enabled: true,
|
||||
})
|
||||
}
|
||||
|
||||
await this.getEventTypes()
|
||||
},
|
||||
|
||||
methods: {
|
||||
resTypeFilter: objectSearchMaker('text'),
|
||||
evtTypeFilter: objectSearchMaker('eventType'),
|
||||
constrFilter: objectSearchMaker('text'),
|
||||
getConstraintNameLabel,
|
||||
|
||||
async getEventTypes () {
|
||||
return this.$AutomationAPI.eventTypesList()
|
||||
.then(({ set }) => {
|
||||
this.eventTypes = set
|
||||
const resourceTypes = new Set(set.map(({ resourceType }) => resourceType))
|
||||
this.resourceTypes = [...resourceTypes].map(resourceType => {
|
||||
return {
|
||||
value: resourceType,
|
||||
text: this.getResourceTypeLabel(resourceType),
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('steps:trigger.configurator.failed-fetch-event-types')))
|
||||
},
|
||||
|
||||
addConstraint () {
|
||||
this.item.triggers.constraints.push({
|
||||
name: '',
|
||||
op: '=',
|
||||
values: [''],
|
||||
_showDetails: true,
|
||||
})
|
||||
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
removeConstraint (index) {
|
||||
this.item.triggers.constraints.splice(index, 1)
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
resourceChanged () {
|
||||
this.item.triggers.eventType = null
|
||||
this.item.triggers.constraints = []
|
||||
this.$root.$emit('change-detected')
|
||||
this.updateDefaultName()
|
||||
},
|
||||
|
||||
eventChanged () {
|
||||
if (['onTimestamp', 'onInterval'].includes(this.item.triggers.eventType)) {
|
||||
this.item.triggers.constraints = []
|
||||
this.addConstraint()
|
||||
}
|
||||
|
||||
this.$root.$emit('change-detected')
|
||||
this.updateDefaultName()
|
||||
},
|
||||
|
||||
enabledChanged () {
|
||||
this.$root.$emit('trigger-updated', this.item.node)
|
||||
this.$root.$emit('change-detected')
|
||||
},
|
||||
|
||||
rowClass (item, type) {
|
||||
if (type === 'row') {
|
||||
return item._showDetails ? 'border-thick' : 'border-thick-transparent'
|
||||
} else if (type === 'row-details') {
|
||||
return ''
|
||||
}
|
||||
},
|
||||
|
||||
updateDefaultName () {
|
||||
const { resourceType, eventType } = this.item.triggers
|
||||
|
||||
if (resourceType) {
|
||||
let value = [this.getResourceTypeLabel(resourceType), this.getEventTypeLabel({ eventType })].filter(v => v).join(' - ')
|
||||
value = value.charAt(0).toUpperCase() + value.slice(1)
|
||||
this.$emit('update-default-value', { value, force: !this.item.node.value })
|
||||
}
|
||||
},
|
||||
|
||||
getOptionTypeKey ({ value }) {
|
||||
return value
|
||||
},
|
||||
|
||||
getOptionEventTypeKey ({ eventType }) {
|
||||
return eventType
|
||||
},
|
||||
|
||||
getResourceTypeLabel (resourceType) {
|
||||
if (!resourceType) return ''
|
||||
|
||||
return resourceType
|
||||
.split(':')
|
||||
.map(part => part
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' '),
|
||||
)
|
||||
.join(' - ')
|
||||
},
|
||||
|
||||
getEventTypeLabel ({ eventType = '' } = {}) {
|
||||
if (!eventType) return ''
|
||||
|
||||
return camelToTitle(eventType.replace('on', ''))
|
||||
},
|
||||
|
||||
getConstraintOperatorLabel (op) {
|
||||
const operator = this.constraintOperatorTypes.find(type => type.value === op)
|
||||
return operator ? operator.text : op
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#constraints {
|
||||
tbody tr .delete-btn {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
tbody tr:hover .delete-btn {
|
||||
display: inline-flex !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
319
client/web/workflow/src/components/Configurator/Workflow.vue
Normal file
@@ -0,0 +1,319 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="workflow"
|
||||
:title="$t('editor:workflow-configuration')"
|
||||
size="lg"
|
||||
:hide-header-close="workflow.workflowID === '0'"
|
||||
:no-close-on-backdrop="workflow.workflowID === '0'"
|
||||
:no-close-on-esc="workflow.workflowID === '0'"
|
||||
no-fade
|
||||
body-class="p-0"
|
||||
>
|
||||
<template #modal-title>
|
||||
{{ $t('editor:workflow-configuration') }}
|
||||
</template>
|
||||
|
||||
<div
|
||||
v-if="workflow.workflowID && workflow.workflowID !== '0'"
|
||||
class="d-flex p-3"
|
||||
>
|
||||
<import
|
||||
data-test-id="button-import-workflow"
|
||||
:disabled="importProcessing"
|
||||
@import="$emit('import', $event)"
|
||||
/>
|
||||
|
||||
<export
|
||||
data-test-id="button-export-workflow"
|
||||
:workflows="[workflow.workflowID]"
|
||||
:file-name="workflow.meta.name || workflow.handle"
|
||||
size="lg"
|
||||
class="ml-1"
|
||||
/>
|
||||
|
||||
<c-permissions-button
|
||||
v-if="workflow.canGrant"
|
||||
:title="workflow.meta.name || workflow.handle || workflow.workflowID"
|
||||
:target="workflow.meta.name || workflow.handle || workflow.workflowID"
|
||||
:resource="`corteza::automation:workflow/${workflow.workflowID}`"
|
||||
:button-label="$t('general:permissions')"
|
||||
class="btn-lg ml-1"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="localWorkflow">
|
||||
<b-tabs card>
|
||||
<b-tab
|
||||
:title="$t('general.label')"
|
||||
active
|
||||
>
|
||||
<b-form-group
|
||||
:label="$t('name.label')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<b-form-input
|
||||
v-model="localWorkflow.meta.name"
|
||||
data-test-id="input-label"
|
||||
:placeholder="$t('name.placeholder')"
|
||||
:state="nameState"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
:label="$t('handle.label')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<b-form-input
|
||||
v-model="localWorkflow.handle"
|
||||
data-test-id="input-handle"
|
||||
:state="handleState"
|
||||
:placeholder="$t('handle.placeholder')"
|
||||
/>
|
||||
<b-form-invalid-feedback
|
||||
data-test-id="input-handle-invalid-state"
|
||||
:state="handleState"
|
||||
>
|
||||
{{ $t('handle.invalid-handle-characters') }}
|
||||
</b-form-invalid-feedback>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
:label="$t('description.label')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<b-form-textarea
|
||||
v-model="localWorkflow.meta.description"
|
||||
data-test-id="input-description"
|
||||
:placeholder="$t('description.placeholder')"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
:label="$t('run-as.label')"
|
||||
:description="$t('run-as.description')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<c-input-user
|
||||
v-model="localWorkflow.runAs"
|
||||
data-test-id="select-run-as"
|
||||
:placeholder="$t('run-as.placeholder')"
|
||||
clearable
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group>
|
||||
<b-form-checkbox
|
||||
v-model="localWorkflow.enabled"
|
||||
data-test-id="checkbox-enable-workflow"
|
||||
>
|
||||
{{ $t('general:enabled') }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
:description="$t('sub-workflow.description')"
|
||||
>
|
||||
<b-form-checkbox
|
||||
v-model="localWorkflow.meta.subWorkflow"
|
||||
data-test-id="checkbox-sub-workflow"
|
||||
>
|
||||
{{ $t('sub-workflow.label') }}
|
||||
</b-form-checkbox>
|
||||
</b-form-group>
|
||||
</b-tab>
|
||||
|
||||
<b-tab :title="$t('labels.label')">
|
||||
<namespace-module-selector
|
||||
:namespace-labels="localWorkflow?.labels?.ref_namespace || []"
|
||||
:module-labels="localWorkflow?.labels?.ref_module || []"
|
||||
@change="handleLabelsChange"
|
||||
/>
|
||||
</b-tab>
|
||||
</b-tabs>
|
||||
</div>
|
||||
|
||||
<template #modal-footer>
|
||||
<div class="d-flex w-100">
|
||||
<c-input-confirm
|
||||
v-if="workflow.canDeleteWorkflow && !isDeleted"
|
||||
size="md"
|
||||
size-confirm="md"
|
||||
variant="danger"
|
||||
:processing="processingDelete"
|
||||
:text="$t('editor:delete')"
|
||||
:borderless="false"
|
||||
@confirmed="$emit('delete')"
|
||||
/>
|
||||
|
||||
<c-input-confirm
|
||||
v-else-if="isDeleted"
|
||||
size="md"
|
||||
size-confirm="md"
|
||||
variant="light"
|
||||
:processing="processingDelete"
|
||||
:text="$t('editor:undelete')"
|
||||
:borderless="false"
|
||||
@confirmed="$emit('undelete')"
|
||||
/>
|
||||
|
||||
<b-button
|
||||
v-if="workflow.workflowID === '0'"
|
||||
variant="light"
|
||||
@click="$router.back()"
|
||||
>
|
||||
{{ $t('editor:back') }}
|
||||
</b-button>
|
||||
|
||||
<div class="d-flex ml-auto">
|
||||
<b-button
|
||||
v-if="workflow.workflowID !== '0'"
|
||||
variant="light"
|
||||
class="ml-auto"
|
||||
@click="handleCancel"
|
||||
>
|
||||
{{ $t('general:cancel') }}
|
||||
</b-button>
|
||||
|
||||
<c-button-submit
|
||||
data-test-id="button-save-workflow"
|
||||
:disabled="isSaveDisabled"
|
||||
:processing="processingSave"
|
||||
:text="$t('editor:save')"
|
||||
class="ml-1"
|
||||
@submit="handleSave"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { handle, components } from '@cortezaproject/corteza-vue'
|
||||
import { automation } from '@cortezaproject/corteza-js'
|
||||
import Import from '../Import'
|
||||
import Export from '../Export'
|
||||
import NamespaceModuleSelector from '../NamespaceModuleSelector'
|
||||
|
||||
const { CInputUser } = components
|
||||
|
||||
export default {
|
||||
i18nOptions: {
|
||||
namespaces: 'configurator',
|
||||
},
|
||||
|
||||
components: {
|
||||
CInputUser,
|
||||
Import,
|
||||
Export,
|
||||
NamespaceModuleSelector,
|
||||
},
|
||||
|
||||
props: {
|
||||
workflow: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
|
||||
canCreate: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
processingSave: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
processingDelete: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
importProcessing: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
localWorkflow: null,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
nameState () {
|
||||
return this.localWorkflow?.meta?.name ? null : false
|
||||
},
|
||||
|
||||
handleState () {
|
||||
return this.localWorkflow ? handle.handleState(this.localWorkflow.handle) : null
|
||||
},
|
||||
|
||||
canUpdateWorkflow () {
|
||||
return this.workflow.workflowID === '0' ? this.canCreate : this.workflow.canUpdateWorkflow
|
||||
},
|
||||
|
||||
isSaveDisabled () {
|
||||
return !this.canUpdateWorkflow || [this.nameState, this.handleState].includes(false)
|
||||
},
|
||||
|
||||
isDeleted () {
|
||||
return this.workflow.deletedAt
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
watch: {
|
||||
workflow: {
|
||||
handler (newWorkflow) {
|
||||
if (!newWorkflow) {
|
||||
this.localWorkflow = null
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new Workflow instance from the existing workflow
|
||||
// This properly clones the workflow and avoids circular references
|
||||
this.localWorkflow = new automation.Workflow(newWorkflow)
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleLabelsChange ({ namespaceLabels, moduleLabels }) {
|
||||
if (!this.localWorkflow.labels) {
|
||||
this.localWorkflow.labels = {}
|
||||
}
|
||||
|
||||
// Store labels directly - no transformation needed
|
||||
if (namespaceLabels.length > 0) {
|
||||
this.localWorkflow.labels.ref_namespace = namespaceLabels
|
||||
} else {
|
||||
delete this.localWorkflow.labels.ref_namespace
|
||||
}
|
||||
|
||||
if (moduleLabels.length > 0) {
|
||||
this.localWorkflow.labels.ref_module = moduleLabels
|
||||
} else {
|
||||
delete this.localWorkflow.labels.ref_module
|
||||
}
|
||||
},
|
||||
|
||||
handleSave () {
|
||||
// Emit save event with the local workflow copy
|
||||
this.$emit('save', this.localWorkflow)
|
||||
// Close the modal after save
|
||||
this.$bvModal.hide('workflow')
|
||||
},
|
||||
|
||||
handleCancel () {
|
||||
// Reset local workflow to original workflow data
|
||||
this.localWorkflow = new automation.Workflow(this.workflow)
|
||||
// Close the modal
|
||||
this.$bvModal.hide('workflow')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
25
client/web/workflow/src/components/Configurator/base.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
|
||||
edges: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
|
||||
outEdges: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
|
||||
isSubworkflow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
72
client/web/workflow/src/components/Configurator/basic.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group
|
||||
:label="$t('general:label.label')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<b-form-input
|
||||
v-model="label"
|
||||
@input="$emit('update-value', $event)"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<!-- <b-form-group
|
||||
v-if="getSelectedItemConfigJSON"
|
||||
label="Config"
|
||||
>
|
||||
<b-form-textarea
|
||||
v-model="getSelectedItemConfigJSON"
|
||||
rows="10"
|
||||
/>
|
||||
</b-form-group> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import base from './base'
|
||||
|
||||
export default {
|
||||
extends: base,
|
||||
|
||||
computed: {
|
||||
// Ignores exclusiveGateway indexes (#n)
|
||||
label: {
|
||||
get () {
|
||||
if (this.getSourceType) {
|
||||
if (this.getSourceType === 'gatewayExclusive') {
|
||||
/* eslint-disable no-unused-vars */
|
||||
const [edgeID, ...rest] = this.item.node.value.split(' - ')
|
||||
return rest.join(' - ')
|
||||
}
|
||||
}
|
||||
|
||||
return this.item.node.value
|
||||
},
|
||||
|
||||
set (label) {
|
||||
if (this.getSourceType) {
|
||||
if (this.getSourceType === 'gatewayExclusive') {
|
||||
/* eslint-disable no-unused-vars */
|
||||
const [edgeID, ...rest] = this.item.node.value.split(' - ')
|
||||
const newLabel = [edgeID]
|
||||
if (label) {
|
||||
newLabel.push(label)
|
||||
}
|
||||
label = newLabel.join(' - ')
|
||||
}
|
||||
}
|
||||
|
||||
this.item.node.value = label
|
||||
},
|
||||
},
|
||||
|
||||
getSourceType () {
|
||||
const { source } = this.item.node
|
||||
if (source && source.style) {
|
||||
return source.style
|
||||
}
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
99
client/web/workflow/src/components/Configurator/index.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div
|
||||
class="d-flex flex-column"
|
||||
>
|
||||
<b-card
|
||||
v-if="kind !== 'Content'"
|
||||
class="flex-grow-1 border-bottom border-light rounded-0"
|
||||
>
|
||||
<b-card-header
|
||||
header-tag="header"
|
||||
class="p-0 mb-3"
|
||||
>
|
||||
<h5
|
||||
class="mb-0"
|
||||
>
|
||||
{{ $t('general:general') }}
|
||||
</h5>
|
||||
</b-card-header>
|
||||
<b-card-body
|
||||
class="p-0"
|
||||
>
|
||||
<basic
|
||||
:item="item"
|
||||
@update-value="$emit('update-value', $event)"
|
||||
/>
|
||||
</b-card-body>
|
||||
</b-card>
|
||||
|
||||
<component
|
||||
:is="stepComponent"
|
||||
v-if="stepComponent"
|
||||
:item.sync="item"
|
||||
:edges.sync="edges"
|
||||
:out-edges="outEdges"
|
||||
:is-subworkflow="isSubworkflow"
|
||||
@update-value="$emit('update-value', $event)"
|
||||
@update-default-value="updateDefaultName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import base from './base'
|
||||
import basic from './basic'
|
||||
import * as Configurators from './loader'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
...Configurators,
|
||||
basic,
|
||||
},
|
||||
|
||||
extends: base,
|
||||
|
||||
data () {
|
||||
return {
|
||||
collapse: {
|
||||
basic: true,
|
||||
configurator: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
stepComponent () {
|
||||
return Configurators[this.kind]
|
||||
},
|
||||
|
||||
kind () {
|
||||
const { kind, ref } = this.item.config
|
||||
|
||||
if (kind === 'exec-workflow') {
|
||||
return 'ExecWorkflow'
|
||||
}
|
||||
|
||||
if (kind === 'error-handler') {
|
||||
return 'ErrorHandler'
|
||||
}
|
||||
|
||||
if (kind === 'visual' && ref === 'content') {
|
||||
return 'Content'
|
||||
}
|
||||
|
||||
if (kind) {
|
||||
return kind.charAt(0).toUpperCase() + kind.slice(1)
|
||||
}
|
||||
|
||||
return undefined
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateDefaultName ({ value, force = false }) {
|
||||
if (force || this.item.config.defaultName || this.item.config.defaultName === undefined) {
|
||||
this.$emit('update-default-value', value)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
13
client/web/workflow/src/components/Configurator/loader.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export { default as Workflow } from './Workflow'
|
||||
export { default as Expressions } from './Expressions'
|
||||
export { default as Function } from './Function'
|
||||
export { default as Iterator } from './Iterator'
|
||||
export { default as Trigger } from './Trigger'
|
||||
export { default as Error } from './Error'
|
||||
export { default as Gateway } from './Gateway'
|
||||
export { default as Edge } from './Edge'
|
||||
export { default as Prompt } from './Prompt'
|
||||
export { default as Delay } from './Delay'
|
||||
export { default as ExecWorkflow } from './ExecWorkflow'
|
||||
export { default as ErrorHandler } from './ErrorHandler'
|
||||
export { default as Content } from './Content'
|
||||
87
client/web/workflow/src/components/Export.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<b-button
|
||||
:variant="variant"
|
||||
:size="size"
|
||||
@click="jsonExport(workflows)"
|
||||
>
|
||||
<slot />
|
||||
{{ $t('general:export') }}
|
||||
</b-button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { saveAs } from 'file-saver'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
workflows: {
|
||||
type: Array,
|
||||
default: () => ([]),
|
||||
},
|
||||
|
||||
fileName: {
|
||||
type: String,
|
||||
default: 'workflows-export',
|
||||
},
|
||||
|
||||
size: {
|
||||
type: String,
|
||||
default: 'md',
|
||||
},
|
||||
|
||||
variant: {
|
||||
type: String,
|
||||
default: 'light',
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
async jsonExport (workflowID = []) {
|
||||
const triggers = {}
|
||||
let workflows = []
|
||||
|
||||
// Get workflow triggers
|
||||
await this.$AutomationAPI.triggerList({ workflowID, disabled: 1 })
|
||||
.then(({ set = [] }) => {
|
||||
set.forEach(({ workflowID, resourceType, eventType, constraints, enabled, stepID, meta }) => {
|
||||
if (!triggers[workflowID]) {
|
||||
triggers[workflowID] = []
|
||||
}
|
||||
|
||||
triggers[workflowID].push({
|
||||
resourceType,
|
||||
eventType,
|
||||
constraints,
|
||||
enabled,
|
||||
stepID,
|
||||
meta,
|
||||
})
|
||||
})
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:failed-fetch-triggers')))
|
||||
|
||||
// Get workflows, add related triggers
|
||||
await this.$AutomationAPI.workflowList({ workflowID, disabled: 1, subWorkflow: 1 })
|
||||
.then(({ set = [] }) => {
|
||||
workflows = set.map(({ workflowID, handle, enabled, keepSessions, steps, paths, meta }) => {
|
||||
return {
|
||||
handle,
|
||||
enabled,
|
||||
meta,
|
||||
keepSessions,
|
||||
steps,
|
||||
paths,
|
||||
triggers: triggers[workflowID],
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(this.toastErrorHandler(this.$t('notification:failed-fetch-workflows')))
|
||||
|
||||
// Save file
|
||||
const blob = new Blob([JSON.stringify({ workflows }, null, 2)], { type: 'application/json' })
|
||||
const filename = this.fileName.replace(/[/\\?%*:|"<>]/g, '')
|
||||
saveAs(blob, `${filename}.json`)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
100
client/web/workflow/src/components/ExpressionEditor.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div
|
||||
class="position-relative"
|
||||
>
|
||||
<c-ace-editor
|
||||
v-model="expressionValue"
|
||||
:lang="lang"
|
||||
:min-height="minHeight"
|
||||
:show-line-numbers="showLineNumbers"
|
||||
:font-size="fontSize"
|
||||
:show-popout="showPopout"
|
||||
:auto-complete="autoComplete"
|
||||
:border="border"
|
||||
:auto-complete-suggestions="expressionAutoCompleteValues"
|
||||
resizable
|
||||
v-on="$listeners"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
import { EXPRESSION_EDITOR_AUTO_COMPLETE_VALUES } from '../lib/editor-auto-complete.js'
|
||||
|
||||
const { CAceEditor } = components
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CAceEditor,
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
lang: {
|
||||
type: String,
|
||||
default: 'text',
|
||||
},
|
||||
|
||||
minHeight: {
|
||||
type: String,
|
||||
default: '6rem',
|
||||
},
|
||||
|
||||
showLineNumbers: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: '14px',
|
||||
},
|
||||
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
|
||||
showPopout: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
|
||||
autoComplete: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
expressionAutoCompleteValues: EXPRESSION_EDITOR_AUTO_COMPLETE_VALUES,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
expressionValue: {
|
||||
get () {
|
||||
return this.value
|
||||
},
|
||||
|
||||
set (value = '') {
|
||||
this.$emit('update:value', value)
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.popout {
|
||||
z-index: 7;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
||||
187
client/web/workflow/src/components/ExpressionTable.vue
Normal file
@@ -0,0 +1,187 @@
|
||||
<template>
|
||||
<div class="table-responsive">
|
||||
<b-row class="header pl-4">
|
||||
<b-col
|
||||
v-for="(field, index) in fields"
|
||||
:key="index"
|
||||
:class="`py-2 ${field.thClass}`"
|
||||
>
|
||||
<label>{{ field.label }}</label>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<draggable
|
||||
:list="items"
|
||||
handle=".grab"
|
||||
@end="$root.$emit('change-detected')"
|
||||
>
|
||||
<div
|
||||
v-for="(item, index) in items"
|
||||
:key="index"
|
||||
>
|
||||
<b-row
|
||||
class="d-flex justify-content-between align-items-center pointer expr-item"
|
||||
no-gutters
|
||||
@click="$set(item, '_showDetails', !item._showDetails)"
|
||||
>
|
||||
<div class="p-2 grab">
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'bars']"
|
||||
class="text-secondary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<b-col
|
||||
v-for="(field, i) in fields"
|
||||
:key="i"
|
||||
class="text-truncate p-2"
|
||||
>
|
||||
<div
|
||||
v-if="field.key === 'expr'"
|
||||
class="d-flex justify-content-between align-items-center"
|
||||
>
|
||||
<samp class="text-truncate">{{ item[field.key] }}</samp>
|
||||
|
||||
<c-input-confirm
|
||||
v-if="item._showDetails"
|
||||
show-icon
|
||||
@confirmed="$emit('remove', index)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<var
|
||||
v-else
|
||||
class=""
|
||||
>
|
||||
{{ field.formatter ? field.formatter(item) : item[field.key] }}
|
||||
</var>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-if="item._showDetails"
|
||||
class="mb-3 px-3"
|
||||
>
|
||||
<div class="arrow-up" />
|
||||
|
||||
<b-card
|
||||
class="bg-light"
|
||||
body-class="px-4 pb-3"
|
||||
>
|
||||
<b-form-group
|
||||
label-class="text-primary"
|
||||
>
|
||||
<b-form-input
|
||||
v-model="item.target"
|
||||
placeholder="Target"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
label-class="text-primary"
|
||||
:description="getTypeDescription(item.type)"
|
||||
>
|
||||
<c-input-select
|
||||
v-model="item.type"
|
||||
:options="types"
|
||||
:get-option-key="getOptionKey"
|
||||
:clearable="false"
|
||||
:filter="varFilter"
|
||||
append-to-body
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
class="mb-0"
|
||||
>
|
||||
<expression-editor
|
||||
v-model="item[valueField]"
|
||||
show-line-numbers
|
||||
@open="$emit('open-editor', index)"
|
||||
@input="$root.$emit('change-detected')"
|
||||
/>
|
||||
</b-form-group>
|
||||
</b-card>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</draggable>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ExpressionEditor from './ExpressionEditor.vue'
|
||||
import { objectSearchMaker } from '../lib/filter'
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ExpressionEditor,
|
||||
draggable,
|
||||
},
|
||||
|
||||
props: {
|
||||
valueField: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
items: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
|
||||
types: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
varFilter: objectSearchMaker('text'),
|
||||
|
||||
getTypeDescription (type) {
|
||||
// This will be moved to backend field type information
|
||||
const typeDescriptions = {
|
||||
ID: 'Make sure to provide the ID in double quotes if you\'re using a literal value. Example "123"',
|
||||
}
|
||||
|
||||
return typeDescriptions[type]
|
||||
},
|
||||
|
||||
getOptionKey (type) {
|
||||
return type
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header {
|
||||
background-color: var(--light);
|
||||
|
||||
label {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.expr-item:hover {
|
||||
background-color: var(--light);
|
||||
|
||||
.grab > * {
|
||||
color: var(--secondary) !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
87
client/web/workflow/src/components/Help.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group
|
||||
label-class="sticky-top bg-white pb-0 text-primary"
|
||||
class="p-2 mb-0"
|
||||
>
|
||||
<template #label>
|
||||
<h5
|
||||
class="d-flex align-items-center p-2 mb-0"
|
||||
>
|
||||
{{ $t('help:basic-controls-label') }}
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'mouse']"
|
||||
class="ml-2"
|
||||
/>
|
||||
</h5>
|
||||
</template>
|
||||
<b-table
|
||||
:items="basicControls"
|
||||
fixed
|
||||
thead-class="d-none"
|
||||
/>
|
||||
</b-form-group>
|
||||
<b-form-group
|
||||
label-class="sticky-top bg-white pb-0 text-primary"
|
||||
class="p-2 mb-0"
|
||||
>
|
||||
<template #label>
|
||||
<h5
|
||||
class="d-flex align-items-center pt-2 px-2 mb-0"
|
||||
>
|
||||
{{ $t('help:keyboard-shortcuts-label') }}
|
||||
<font-awesome-icon
|
||||
:icon="['fas', 'keyboard']"
|
||||
class="ml-2"
|
||||
/>
|
||||
</h5>
|
||||
</template>
|
||||
<small
|
||||
class="ml-2"
|
||||
>
|
||||
{{ $t('help:mac-users') }}
|
||||
</small>
|
||||
<b-table
|
||||
:items="keyboardShortcuts"
|
||||
fixed
|
||||
thead-class="d-none"
|
||||
/>
|
||||
</b-form-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
basicControls: [
|
||||
{ action: this.$t('help:basic-controls.action.select'), shortcut: this.$t('help:basic-controls.shortcut.left-click.label') },
|
||||
{ action: this.$t('help:basic-controls.action.select-multiple'), shortcut: this.$t('help:basic-controls.shortcut.left-click.drag') },
|
||||
{ action: this.$t('help:basic-controls.action.deselect'), shortcut: this.$t('help:basic-controls.shortcut.left-click.background') },
|
||||
{ action: this.$t('help:basic-controls.action.pan'), shortcut: this.$t('help:basic-controls.shortcut.pan') },
|
||||
{ action: this.$t('help:basic-controls.action.zoom-in/out'), shortcut: this.$t('help:basic-controls.shortcut.zoom') },
|
||||
],
|
||||
|
||||
keyboardShortcuts: [
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.select-all'), shortcut: 'Ctrl + A' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.add-selection'), shortcut: `Ctrl + ${this.$t('help:keyboard-shortcuts.shortcut.left-mouse-button')}` },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.delete-selected-elements'), shortcut: 'Delete or Backspace' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.reset-view-to-start-point'), shortcut: 'Ctrl + Space' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.undo'), shortcut: 'Ctrl + Z' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.redo'), shortcut: 'Ctrl + Shift + Z' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.cut'), shortcut: 'Ctrl + X' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.copy'), shortcut: 'Ctrl + C' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.paste'), shortcut: 'Ctrl + V' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.save'), shortcut: 'Ctrl + S' },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.nudge'), shortcut: `${this.$t('help:keyboard-shortcuts.shortcut.arrow-keys')} Shift ${this.$t('help:keyboard-shortcuts.shortcut.adjust-distance')}` },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.hide-guides'), shortcut: `${this.$t('help:keyboard-shortcuts.shortcut.hold')} Alt` },
|
||||
{ action: this.$t('help:keyboard-shortcuts.action.show-help'), shortcut: 'Shift + ?' },
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
108
client/web/workflow/src/components/Import.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div class="d-flex">
|
||||
<b-button
|
||||
variant="light"
|
||||
size="lg"
|
||||
class="flex-fill"
|
||||
@click="show = true"
|
||||
>
|
||||
{{ $t('general:import.label') }}
|
||||
</b-button>
|
||||
|
||||
<b-modal
|
||||
id="import"
|
||||
v-model="show"
|
||||
size="lg"
|
||||
:title="$t('general:import.json')"
|
||||
ok-only
|
||||
no-fade
|
||||
class="d-none"
|
||||
@ok="$emit('import', workflows)"
|
||||
>
|
||||
<b-form-group
|
||||
:description="$t('general:import.reassign-run-as')"
|
||||
class="mb-0"
|
||||
>
|
||||
<b-form-file
|
||||
:placeholder="$t('general:import.upload-files')"
|
||||
@change="fileUpload"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<template #modal-footer>
|
||||
<b-button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
:disabled="!workflows.length || processing"
|
||||
class="d-flex justify-content-center align-items-center"
|
||||
@click="$emit('import', workflows)"
|
||||
>
|
||||
<b-spinner
|
||||
v-if="processing"
|
||||
small
|
||||
type="grow"
|
||||
/>
|
||||
|
||||
<span
|
||||
v-else
|
||||
>
|
||||
{{ $t('general:import.label') }}
|
||||
</span>
|
||||
</b-button>
|
||||
</template>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
show: false,
|
||||
workflows: [],
|
||||
processing: false,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
fileUpload (e = {}) {
|
||||
const { files = [] } = (e.type === 'drop' ? e.dataTransfer : e.target) || {}
|
||||
|
||||
if (files[0]) {
|
||||
this.processing = true
|
||||
const reader = new FileReader()
|
||||
|
||||
reader.readAsText(files[0])
|
||||
|
||||
reader.onload = (evt) => {
|
||||
try {
|
||||
const { workflows = [] } = JSON.parse(evt.target.result)
|
||||
this.workflows = workflows
|
||||
} catch (err) {
|
||||
err.message = this.$t('notification:failed-load-file')
|
||||
this.toastErrorHandler(this.$t('notification:general.warning'))(err)
|
||||
} finally {
|
||||
this.processing = false
|
||||
}
|
||||
}
|
||||
|
||||
reader.onerror = () => {
|
||||
this.toastErrorHandler(this.$t('notification:failed-load-file'))
|
||||
this.processing = false
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
360
client/web/workflow/src/components/NamespaceModuleSelector.vue
Normal file
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group
|
||||
:label="$t('filter.namespace.label')"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<c-input-select
|
||||
class="namespace-selector"
|
||||
:options="namespace.options"
|
||||
:value="namespace.values"
|
||||
:get-option-label="getNamespaceOptionLabel"
|
||||
:get-option-key="n => `corteza::compose:namespace/${n.namespaceID}`"
|
||||
:reduce="n => `corteza::compose:namespace/${n.namespaceID}`"
|
||||
:placeholder="$t('filter.namespace.placeholder')"
|
||||
:loading="namespace.processing"
|
||||
:filterable="false"
|
||||
multiple
|
||||
@search="searchNamespaces"
|
||||
@input="updateNamespaces"
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group
|
||||
v-for="ns in namespace.values"
|
||||
:key="ns"
|
||||
:label="getModuleLabel(ns)"
|
||||
label-class="text-primary"
|
||||
>
|
||||
<c-input-select
|
||||
class="module-selector"
|
||||
:options="getModulesForNamespace(ns.split('/')[1])"
|
||||
:value="getModuleValuesForNamespace(ns.split('/')[1])"
|
||||
:get-option-key="m => `corteza::compose:module/${m.namespaceID}/${m.moduleID}`"
|
||||
:get-option-label="getModuleOptionLabel"
|
||||
:reduce="m => `corteza::compose:module/${m.namespaceID}/${m.moduleID}`"
|
||||
:placeholder="$t('filter.module.placeholder')"
|
||||
:loading="module.processing"
|
||||
:filterable="false"
|
||||
multiple
|
||||
@search="query => searchModulesForNamespace(query, ns.split('/')[1])"
|
||||
@input="modules => updateModulesForNamespace(modules, ns.split('/')[1])"
|
||||
/>
|
||||
</b-form-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { debounce } from 'lodash'
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
|
||||
const { CInputSelect } = components
|
||||
|
||||
export default {
|
||||
name: 'NamespaceModuleSelector',
|
||||
|
||||
components: {
|
||||
CInputSelect,
|
||||
},
|
||||
|
||||
i18nOptions: {
|
||||
namespaces: 'general',
|
||||
},
|
||||
|
||||
props: {
|
||||
/**
|
||||
* Initial namespace labels in format: ["corteza::compose:namespace/id1", ...]
|
||||
*/
|
||||
namespaceLabels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
/**
|
||||
* Initial module labels in format: ["corteza::compose:module/nsID/modID", ...]
|
||||
*/
|
||||
moduleLabels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
namespace: {
|
||||
processing: false,
|
||||
values: [],
|
||||
options: [],
|
||||
filter: {
|
||||
query: null,
|
||||
limit: 20,
|
||||
sort: 'name DESC',
|
||||
},
|
||||
},
|
||||
|
||||
module: {
|
||||
processing: false,
|
||||
values: [],
|
||||
options: [],
|
||||
filter: {
|
||||
query: null,
|
||||
limit: 20,
|
||||
sort: 'name DESC',
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
modulesByNamespace () {
|
||||
const grouped = {}
|
||||
|
||||
this.module.options.forEach(module => {
|
||||
if (!grouped[module.namespaceID]) {
|
||||
grouped[module.namespaceID] = []
|
||||
}
|
||||
grouped[module.namespaceID].push(module)
|
||||
})
|
||||
|
||||
return grouped
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
namespaceLabels: {
|
||||
handler (newVal) {
|
||||
if (newVal && newVal.length > 0 && this.namespace.options.length > 0) {
|
||||
this.initializeFromProps()
|
||||
}
|
||||
},
|
||||
immediate: false,
|
||||
},
|
||||
},
|
||||
|
||||
created () {
|
||||
this.fetchNamespaces().then(() => {
|
||||
this.initializeFromProps()
|
||||
})
|
||||
},
|
||||
|
||||
methods: {
|
||||
initializeFromProps () {
|
||||
// Set namespace values directly from labels, filtering out malformed entries
|
||||
this.namespace.values = (this.namespaceLabels || []).filter(label => this.isValidLabel(label, 2))
|
||||
|
||||
// Set module values directly from labels
|
||||
if (this.namespace.values.length > 0) {
|
||||
this.fetchModules().then(() => {
|
||||
this.module.values = (this.moduleLabels || []).filter(label => this.isValidLabel(label, 3))
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
fetchNamespaces () {
|
||||
this.namespace.processing = true
|
||||
|
||||
return this.$ComposeAPI.namespaceList(this.namespace.filter).then(({ set = [] } = {}) => {
|
||||
const namespacePromises = []
|
||||
|
||||
// Fetch any missing namespaces from initial labels
|
||||
if (this.namespaceLabels && this.namespaceLabels.length > 0 && !this.namespace.filter.query) {
|
||||
const namespaceIDs = this.namespaceLabels.map(label => label.split('/')[1]).filter(Boolean)
|
||||
|
||||
namespaceIDs.forEach(namespaceID => {
|
||||
if (!set.some(n => n.namespaceID === namespaceID)) {
|
||||
namespacePromises.push(
|
||||
this.$ComposeAPI.namespaceRead({ namespaceID })
|
||||
.then(n => [n])
|
||||
.catch(() => [{ namespaceID }]),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.all(namespacePromises).then(results => {
|
||||
this.namespace.options = [...set, ...results.flat()].sort((a, b) =>
|
||||
(a.name || '').localeCompare(b.name || ''),
|
||||
)
|
||||
}).catch(() => {
|
||||
this.namespace.options = []
|
||||
})
|
||||
}).finally(() => {
|
||||
this.namespace.processing = false
|
||||
})
|
||||
},
|
||||
|
||||
fetchModules () {
|
||||
if (!this.namespace.values || this.namespace.values.length === 0) {
|
||||
this.module.options = []
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
this.module.processing = true
|
||||
|
||||
// Extract namespace IDs from label strings
|
||||
const namespaceIDs = this.namespace.values.map(label => label.split('/')[1])
|
||||
|
||||
const promises = namespaceIDs.map(namespaceID =>
|
||||
this.$ComposeAPI.moduleList({
|
||||
namespaceID,
|
||||
...this.module.filter,
|
||||
}).then(({ set }) => set),
|
||||
)
|
||||
|
||||
return Promise.all(promises).then(results => {
|
||||
const set = results.flat()
|
||||
|
||||
// Fetch any selected modules missing from the listed page (e.g. when there are more modules in a namespace than the page limit).
|
||||
const missingPromises = []
|
||||
if (this.moduleLabels && this.moduleLabels.length > 0 && !this.module.filter.query) {
|
||||
this.moduleLabels.forEach(label => {
|
||||
const parts = label.split('/')
|
||||
const namespaceID = parts[1]
|
||||
const moduleID = parts[2]
|
||||
if (!namespaceID || !moduleID) return
|
||||
if (set.some(m => m.moduleID === moduleID)) return
|
||||
missingPromises.push(
|
||||
this.$ComposeAPI.moduleRead({ namespaceID, moduleID })
|
||||
.then(m => [m])
|
||||
.catch(() => [{ namespaceID, moduleID }]),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.all(missingPromises).then(extra => {
|
||||
this.module.options = [...set, ...extra.flat()]
|
||||
})
|
||||
}).catch(() => {
|
||||
this.module.options = []
|
||||
}).finally(() => {
|
||||
this.module.processing = false
|
||||
})
|
||||
},
|
||||
|
||||
searchNamespaces: debounce(function (query) {
|
||||
if (query !== this.namespace.filter.query) {
|
||||
this.namespace.filter.query = query
|
||||
}
|
||||
this.fetchNamespaces()
|
||||
}, 300),
|
||||
|
||||
searchModulesForNamespace: debounce(function (query, namespaceID) {
|
||||
if (query !== this.module.filter.query) {
|
||||
this.module.filter.query = query
|
||||
}
|
||||
this.fetchModules()
|
||||
}, 300),
|
||||
|
||||
updateNamespaces (namespaceLabels) {
|
||||
this.namespace.values = (namespaceLabels || []).filter(label => this.isValidLabel(label, 2))
|
||||
|
||||
if (this.namespace.values.length > 0) {
|
||||
// Filter out modules from namespaces that are no longer selected,
|
||||
// and drop any malformed module labels at the same time.
|
||||
const selectedNsIDs = new Set(this.namespace.values.map(label => label.split('/')[1]))
|
||||
this.module.values = this.module.values.filter(moduleLabel => {
|
||||
if (!this.isValidLabel(moduleLabel, 3)) return false
|
||||
const nsID = moduleLabel.split('/')[1]
|
||||
return selectedNsIDs.has(nsID)
|
||||
})
|
||||
|
||||
this.fetchModules()
|
||||
} else {
|
||||
this.module.options = []
|
||||
this.module.values = []
|
||||
}
|
||||
|
||||
this.emitChange()
|
||||
},
|
||||
|
||||
updateModulesForNamespace (moduleLabels, namespaceID) {
|
||||
// Remove old modules for this namespace
|
||||
this.module.values = this.module.values.filter(label => {
|
||||
const nsID = label.split('/')[1]
|
||||
return nsID !== namespaceID
|
||||
})
|
||||
|
||||
// Add new module labels for this namespace
|
||||
if (moduleLabels && moduleLabels.length > 0) {
|
||||
const valid = moduleLabels.filter(label => this.isValidLabel(label, 3))
|
||||
this.module.values = [...this.module.values, ...valid]
|
||||
}
|
||||
|
||||
this.emitChange()
|
||||
},
|
||||
|
||||
isValidLabel (label, expectedParts) {
|
||||
if (typeof label !== 'string' || !label) return false
|
||||
const parts = label.split('/')
|
||||
if (parts.length < expectedParts) return false
|
||||
for (let i = 1; i < expectedParts; i++) {
|
||||
if (!parts[i] || parts[i] === 'undefined') return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
emitChange () {
|
||||
// Values are already in label format - emit directly
|
||||
this.$emit('change', {
|
||||
namespaceLabels: this.namespace.values,
|
||||
moduleLabels: this.module.values,
|
||||
})
|
||||
},
|
||||
|
||||
getNamespaceOptionLabel ({ name, slug, namespaceID } = {}) {
|
||||
return name || slug || namespaceID
|
||||
},
|
||||
|
||||
getModuleOptionLabel ({ name, handle, moduleID } = {}) {
|
||||
return name || handle || moduleID
|
||||
},
|
||||
|
||||
getModuleLabel (namespaceLabel) {
|
||||
const namespaceID = namespaceLabel.split('/')[1]
|
||||
const namespace = this.namespace.options.find(n => n.namespaceID === namespaceID)
|
||||
const nsLabel = namespace ? this.getNamespaceOptionLabel(namespace) : namespaceID
|
||||
|
||||
return this.$t('filter.module.template', { namespace: nsLabel })
|
||||
},
|
||||
|
||||
getModulesForNamespace (namespaceID) {
|
||||
return this.modulesByNamespace[namespaceID] || []
|
||||
},
|
||||
|
||||
getModuleValuesForNamespace (namespaceID) {
|
||||
return this.module.values.filter(label => {
|
||||
const nsID = label.split('/')[1]
|
||||
return nsID === namespaceID
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the selector to empty state
|
||||
*/
|
||||
reset () {
|
||||
this.namespace.values = []
|
||||
this.module.values = []
|
||||
this.module.options = []
|
||||
this.emitChange()
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Style namespace select tags (primary blue) */
|
||||
::v-deep .namespace-selector .vs__selected {
|
||||
background-color: var(--primary) !important;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
::v-deep .namespace-selector .vs__deselect {
|
||||
fill: white !important;
|
||||
}
|
||||
|
||||
/* Style module select tags (extra-light with dark text) */
|
||||
::v-deep .module-selector .vs__selected {
|
||||
background-color: var(--extra-light) !important;
|
||||
color: var(--dark) !important;
|
||||
}
|
||||
</style>
|
||||
85
client/web/workflow/src/components/Tooltip.vue
Normal file
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<b-tooltip
|
||||
:id="kind"
|
||||
:target="kind"
|
||||
triggers="hover"
|
||||
placement="right"
|
||||
variant="light"
|
||||
custom-class="ml-2"
|
||||
boundary="window"
|
||||
noninteractive
|
||||
@hide="show = false"
|
||||
@show="show = true"
|
||||
>
|
||||
<transition name="fade">
|
||||
<b-card
|
||||
v-if="show"
|
||||
:img-src="img"
|
||||
:img-alt="kind"
|
||||
img-height="100px"
|
||||
img-top
|
||||
:title="title"
|
||||
class="text-left text-dark"
|
||||
>
|
||||
<b-card-text>
|
||||
{{ text }}
|
||||
</b-card-text>
|
||||
</b-card>
|
||||
</transition>
|
||||
</b-tooltip>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
kind: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
img: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
show: false,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.tooltip.b-tooltip-light > .tooltip-inner {
|
||||
padding: 0;
|
||||
max-width: 20rem;
|
||||
min-width: 20rem;
|
||||
background-color: transparent !important;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.tooltip.b-tooltip-light.bs-tooltip-right .arrow::before {
|
||||
border-right-color: var(--dark) !important;
|
||||
}
|
||||
|
||||
.tooltip.b-tooltip {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
48
client/web/workflow/src/components/WorkflowEditor.c3.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import { default as component } from './WorkflowEditor.vue'
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
const { checkbox } = components.C3.controls
|
||||
|
||||
const props = {
|
||||
workflowObject: {
|
||||
workflowID: '',
|
||||
handle: 'handle',
|
||||
meta: {
|
||||
name: 'Name',
|
||||
description: 'desc',
|
||||
},
|
||||
enabled: true,
|
||||
},
|
||||
workflowTriggers: [],
|
||||
changeDetected: false,
|
||||
canCreate: true,
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'Workflow editor',
|
||||
group: ['Root components'],
|
||||
component,
|
||||
props,
|
||||
|
||||
controls: [
|
||||
checkbox('CanCreate', 'canCreate'),
|
||||
],
|
||||
|
||||
scenarios: [
|
||||
{
|
||||
label: 'Full form',
|
||||
props,
|
||||
},
|
||||
{
|
||||
label: 'Empty form',
|
||||
props: {
|
||||
...props,
|
||||
workflowObject: {
|
||||
...props.workflowObject,
|
||||
enabled: false,
|
||||
name: '',
|
||||
},
|
||||
canCreate: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
2885
client/web/workflow/src/components/WorkflowEditor.vue
Normal file
114
client/web/workflow/src/components/WorkflowFilterModal.vue
Normal file
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<b-modal
|
||||
id="workflow-filter"
|
||||
:title="$t('general:filter.title')"
|
||||
size="lg"
|
||||
no-fade
|
||||
body-class="p-3"
|
||||
@show="onShow"
|
||||
>
|
||||
<namespace-module-selector
|
||||
ref="selector"
|
||||
:namespace-labels="localNamespaceLabels"
|
||||
:module-labels="localModuleLabels"
|
||||
@change="handleChange"
|
||||
/>
|
||||
|
||||
<template #modal-footer>
|
||||
<div class="d-flex gap-1 w-100">
|
||||
<b-button
|
||||
variant="light"
|
||||
@click="handleReset"
|
||||
>
|
||||
{{ $t('general:reset') }}
|
||||
</b-button>
|
||||
|
||||
<div class="d-flex ml-auto gap-1">
|
||||
<b-button
|
||||
variant="light"
|
||||
@click="handleCancel"
|
||||
>
|
||||
{{ $t('general:cancel') }}
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
variant="primary"
|
||||
@click="handleApply"
|
||||
>
|
||||
{{ $t('general:filter.apply') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NamespaceModuleSelector from './NamespaceModuleSelector'
|
||||
|
||||
export default {
|
||||
name: 'WorkflowFilterModal',
|
||||
|
||||
components: {
|
||||
NamespaceModuleSelector,
|
||||
},
|
||||
|
||||
props: {
|
||||
/**
|
||||
* Current namespace labels filter
|
||||
*/
|
||||
namespaceLabels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
|
||||
/**
|
||||
* Current module labels filter
|
||||
*/
|
||||
moduleLabels: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
localNamespaceLabels: [],
|
||||
localModuleLabels: [],
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onShow () {
|
||||
// Reset local state to current props when modal opens
|
||||
this.localNamespaceLabels = [...this.namespaceLabels]
|
||||
this.localModuleLabels = [...this.moduleLabels]
|
||||
},
|
||||
|
||||
handleChange ({ namespaceLabels, moduleLabels }) {
|
||||
this.localNamespaceLabels = namespaceLabels
|
||||
this.localModuleLabels = moduleLabels
|
||||
},
|
||||
|
||||
handleApply () {
|
||||
this.$emit('apply', {
|
||||
namespaceLabels: this.localNamespaceLabels,
|
||||
moduleLabels: this.localModuleLabels,
|
||||
})
|
||||
this.$bvModal.hide('workflow-filter')
|
||||
},
|
||||
|
||||
handleReset () {
|
||||
this.localNamespaceLabels = []
|
||||
this.localModuleLabels = []
|
||||
if (this.$refs.selector) {
|
||||
this.$refs.selector.reset()
|
||||
}
|
||||
},
|
||||
|
||||
handleCancel () {
|
||||
this.$bvModal.hide('workflow-filter')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
127
client/web/workflow/src/components/faIcons.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faCheck,
|
||||
faCheckDouble,
|
||||
faTimes,
|
||||
faHome,
|
||||
faSave,
|
||||
faKeyboard,
|
||||
faMouse,
|
||||
faCog,
|
||||
faChevronLeft,
|
||||
faChevronRight,
|
||||
faChevronDown,
|
||||
faExclamationTriangle,
|
||||
faSearchPlus,
|
||||
faSearchMinus,
|
||||
faThumbtack,
|
||||
faGripHorizontal,
|
||||
faSearch,
|
||||
faExpandAlt,
|
||||
faPlus,
|
||||
faQuestion,
|
||||
faAngleDoubleLeft,
|
||||
faAngleUp,
|
||||
faAngleDown,
|
||||
faAngleRight,
|
||||
faAngleLeft,
|
||||
faEllipsisV,
|
||||
faBars,
|
||||
faFileExport,
|
||||
faToggleOn,
|
||||
faToggleOff,
|
||||
faBold,
|
||||
faItalic,
|
||||
faUnderline,
|
||||
faStrikethrough,
|
||||
faQuoteRight,
|
||||
faCode,
|
||||
faListUl,
|
||||
faListOl,
|
||||
faOutdent,
|
||||
faIndent,
|
||||
faAlignLeft,
|
||||
faAlignCenter,
|
||||
faAlignRight,
|
||||
faAlignJustify,
|
||||
faLink,
|
||||
faRemoveFormat,
|
||||
faParagraph,
|
||||
faTasks,
|
||||
faTable,
|
||||
faBellSlash,
|
||||
faBell as faBellSolid,
|
||||
faFilter,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
import {
|
||||
faEdit,
|
||||
faTrashAlt,
|
||||
faQuestionCircle,
|
||||
faUser,
|
||||
faBell,
|
||||
faEnvelope,
|
||||
faEnvelopeOpen,
|
||||
} from '@fortawesome/free-regular-svg-icons'
|
||||
|
||||
library.add(
|
||||
faEdit,
|
||||
faTrashAlt,
|
||||
faCheck,
|
||||
faCheckDouble,
|
||||
faTimes,
|
||||
faHome,
|
||||
faSave,
|
||||
faKeyboard,
|
||||
faMouse,
|
||||
faCog,
|
||||
faChevronLeft,
|
||||
faChevronRight,
|
||||
faChevronDown,
|
||||
faExclamationTriangle,
|
||||
faQuestionCircle,
|
||||
faSearchPlus,
|
||||
faSearchMinus,
|
||||
faUser,
|
||||
faThumbtack,
|
||||
faSearch,
|
||||
faGripHorizontal,
|
||||
faExpandAlt,
|
||||
faPlus,
|
||||
faQuestion,
|
||||
faAngleDoubleLeft,
|
||||
faAngleRight,
|
||||
faAngleLeft,
|
||||
faEllipsisV,
|
||||
faBars,
|
||||
faFileExport,
|
||||
faToggleOn,
|
||||
faToggleOff,
|
||||
faAngleUp,
|
||||
faAngleDown,
|
||||
faBold,
|
||||
faItalic,
|
||||
faUnderline,
|
||||
faStrikethrough,
|
||||
faQuoteRight,
|
||||
faCode,
|
||||
faListUl,
|
||||
faListOl,
|
||||
faOutdent,
|
||||
faIndent,
|
||||
faAlignLeft,
|
||||
faAlignCenter,
|
||||
faAlignRight,
|
||||
faAlignJustify,
|
||||
faLink,
|
||||
faRemoveFormat,
|
||||
faParagraph,
|
||||
faTasks,
|
||||
faTable,
|
||||
faBell,
|
||||
faBellSolid,
|
||||
faBellSlash,
|
||||
faEnvelope,
|
||||
faEnvelopeOpen,
|
||||
faFilter,
|
||||
)
|
||||
13
client/web/workflow/src/components/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import Vue from 'vue'
|
||||
import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
|
||||
import './faIcons'
|
||||
import PortalVue from 'portal-vue'
|
||||
import { components } from '@cortezaproject/corteza-vue'
|
||||
|
||||
Vue.use(PortalVue)
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon)
|
||||
Vue.component('font-awesome-layers', FontAwesomeLayers)
|
||||
Vue.component('c-permissions-button', components.CPermissionsButton)
|
||||
Vue.component('c-input-confirm', components.CInputConfirm)
|
||||
Vue.component('c-button-submit', components.CButtonSubmit)
|
||||
Vue.component('c-input-select', components.CInputSelect)
|
||||
8
client/web/workflow/src/config-check.js
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
'CortezaAPI',
|
||||
].forEach((cfg) => {
|
||||
if (window[cfg] === undefined) {
|
||||
throw new Error(`Missing or invalid configuration.
|
||||
Make sure there is a public/config.js configuration file with window.${cfg} entry.`)
|
||||
}
|
||||
})
|
||||
6
client/web/workflow/src/console-splash.js
Normal file
@@ -0,0 +1,6 @@
|
||||
/* eslint-disable no-undef */
|
||||
const text = `%c${WEBAPP || 'Corteza Webapp'}, version: ${VERSION}, build time: ${BUILD_TIME}`
|
||||
const style = 'background-color: #1397CB; color: white; padding: 3px 10px; border: 1px solid black; font: Courier'
|
||||
|
||||
/* eslint-disable no-console */
|
||||
console.log(text, style)
|
||||
42
client/web/workflow/src/dev-env.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import BootstrapVue from 'bootstrap-vue'
|
||||
import c3catalogue from './components/C3'
|
||||
import { components, i18n } from '@cortezaproject/corteza-vue'
|
||||
import './components'
|
||||
import './themes'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/c3',
|
||||
name: 'c3',
|
||||
component: components.C3.View,
|
||||
props: { catalogue: c3catalogue },
|
||||
},
|
||||
{ path: '*', redirect: { name: 'c3' } },
|
||||
]
|
||||
|
||||
Vue.use(Router)
|
||||
Vue.use(BootstrapVue)
|
||||
|
||||
export default new Vue({
|
||||
el: '#app',
|
||||
name: 'DevEnv',
|
||||
template: '<router-view/>',
|
||||
router: new Router({
|
||||
mode: 'history',
|
||||
routes,
|
||||
}),
|
||||
i18n: i18n(Vue,
|
||||
{ app: 'corteza-webapp-workflow' },
|
||||
'configurator',
|
||||
'editor',
|
||||
'help',
|
||||
'general',
|
||||
'navigation',
|
||||
'notification',
|
||||
'permissions',
|
||||
'configurator',
|
||||
'steps',
|
||||
),
|
||||
})
|
||||
6
client/web/workflow/src/filters/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import Vue from 'vue'
|
||||
import { filters } from '@cortezaproject/corteza-vue'
|
||||
|
||||
for (const n in filters) {
|
||||
Vue.filter(n, filters[n])
|
||||
}
|
||||
77
client/web/workflow/src/lib/codec.js
Normal file
@@ -0,0 +1,77 @@
|
||||
export function encodeGraph (model, vertices, edges) {
|
||||
const steps = []
|
||||
const paths = {}
|
||||
const triggers = []
|
||||
|
||||
Object.values(model.cells)
|
||||
.filter(cell => {
|
||||
return !!cell.vertex
|
||||
}).forEach(cell => {
|
||||
const triggerEdges = []
|
||||
|
||||
const defaultName = vertices[cell.id].config.defaultName || false
|
||||
|
||||
cell = {
|
||||
id: cell.id,
|
||||
value: cell.value,
|
||||
defaultName,
|
||||
xywh: [
|
||||
cell.geometry.x || 0,
|
||||
cell.geometry.y || 0,
|
||||
cell.geometry.width || 0,
|
||||
cell.geometry.height || 0,
|
||||
],
|
||||
parent: cell.parent.id,
|
||||
edges: (cell.edges || []).forEach(({ id, value, parent, source, target, geometry, style }) => {
|
||||
const edge = {
|
||||
...((edges[id] || {}).config || {}),
|
||||
parentID: source.id,
|
||||
childID: target.id,
|
||||
meta: {
|
||||
label: value || '',
|
||||
description: '',
|
||||
visual: {
|
||||
id,
|
||||
value,
|
||||
parent: parent.id,
|
||||
points: geometry.points,
|
||||
style,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if (vertices[source.id].triggers || vertices[target.id].triggers) {
|
||||
triggerEdges.push(edge)
|
||||
} else if (!paths[id]) {
|
||||
paths[id] = edge
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
if (vertices[cell.id].triggers) {
|
||||
cell.edges = triggerEdges
|
||||
triggers.push({
|
||||
...vertices[cell.id].triggers,
|
||||
stepID: (triggerEdges[0] || { childID: '0' }).childID,
|
||||
enabled: vertices[cell.id].triggers.enabled,
|
||||
constraints: vertices[cell.id].triggers.constraints,
|
||||
meta: {
|
||||
name: cell.value || '',
|
||||
description: '',
|
||||
visual: cell,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
steps.push({
|
||||
...vertices[cell.id].config,
|
||||
meta: {
|
||||
label: cell.value || '',
|
||||
description: '',
|
||||
visual: cell,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return { steps, paths: Object.values(paths), triggers }
|
||||
}
|
||||
21
client/web/workflow/src/lib/constraint.js
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Constraint utility functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts a constraint name to a human-readable label
|
||||
* @param {string} name - The constraint name (e.g., "user.email")
|
||||
* @returns {string} The formatted label (e.g., "User Email")
|
||||
*/
|
||||
export function getConstraintNameLabel (name) {
|
||||
if (!name) return ''
|
||||
|
||||
return name
|
||||
.split('.')
|
||||
.map(s => {
|
||||
const first = s[0] || ''
|
||||
const rest = s.slice(1) || ''
|
||||
return first.toUpperCase() + rest.toLowerCase()
|
||||
})
|
||||
.join(' ')
|
||||
}
|
||||
146
client/web/workflow/src/lib/dry-run.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import { automation } from '@cortezaproject/corteza-js'
|
||||
|
||||
export async function encodeInput (initialScope, ComposeAPI, SystemAPI) {
|
||||
const ev = { args: {} }
|
||||
|
||||
// Types that can and must be fetched
|
||||
const {
|
||||
namespace,
|
||||
oldNamespace,
|
||||
module,
|
||||
oldModule,
|
||||
page,
|
||||
oldPage,
|
||||
record,
|
||||
oldRecord,
|
||||
user,
|
||||
oldUser,
|
||||
role,
|
||||
oldRole,
|
||||
application,
|
||||
oldApplication,
|
||||
} = initialScope
|
||||
|
||||
if (namespace && namespace.value) {
|
||||
await ComposeAPI.namespaceList({ slug: namespace.value })
|
||||
.then(({ set = [] }) => {
|
||||
const [ns] = set
|
||||
if (ns) {
|
||||
ev.args.namespace = { ...ns, resourceType: 'compose:namespace' }
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
if (oldNamespace && oldNamespace.value) {
|
||||
await ComposeAPI.namespaceList({ slug: oldNamespace.value })
|
||||
.then(({ set = [] }) => {
|
||||
const [ns] = set
|
||||
if (ns) {
|
||||
ev.args.oldNamespace = { ...ns, resourceType: 'compose:namespace' }
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
if (module && module.value && ev.args.namespace) {
|
||||
await ComposeAPI.moduleList({ namespaceID: ev.args.namespace.namespaceID, handle: module.value })
|
||||
.then(({ set = [] }) => {
|
||||
const [m] = set
|
||||
if (m) {
|
||||
ev.args.module = { ...m, resourceType: 'compose:module' }
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
if (oldModule && oldModule.value && ev.args.namespace) {
|
||||
await ComposeAPI.moduleList({ namespaceID: ev.args.namespace.namespaceID, handle: oldModule.value })
|
||||
.then(({ set = [] }) => {
|
||||
const [m] = set
|
||||
if (m) {
|
||||
ev.args.oldModule = { ...m, resourceType: 'compose:module' }
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
if (page && page.value && ev.args.namespace) {
|
||||
await ComposeAPI.pageRead({ pageID: page.value, namespaceID: ev.args.namespace.namespaceID })
|
||||
.then(p => {
|
||||
ev.args.page = { ...p }
|
||||
})
|
||||
}
|
||||
|
||||
if (oldPage && oldPage.value && ev.args.namespace) {
|
||||
await ComposeAPI.pageRead({ pageID: page.value, namespaceID: ev.args.namespace.namespaceID })
|
||||
.then(p => {
|
||||
ev.args.oldPage = { ...p }
|
||||
})
|
||||
}
|
||||
|
||||
if (record && record.value && ev.args.module && ev.args.namespace) {
|
||||
await ComposeAPI.recordRead({ recordID: record.value, moduleID: ev.args.module.moduleID, namespaceID: ev.args.namespace.namespaceID })
|
||||
.then(r => {
|
||||
ev.args.record = { ...r, resourceType: 'compose:record' }
|
||||
})
|
||||
}
|
||||
|
||||
if (oldRecord && oldRecord.value && ev.args.module && ev.args.namespace) {
|
||||
await ComposeAPI.recordRead({ recordID: oldRecord.value, moduleID: ev.args.module.moduleID, namespaceID: ev.args.namespace.namespaceID })
|
||||
.then(r => {
|
||||
ev.args.oldRecord = { ...r, resourceType: 'compose:record' }
|
||||
})
|
||||
}
|
||||
|
||||
if (user && user.value) {
|
||||
await SystemAPI.userRead({ userID: user.value })
|
||||
.then(u => {
|
||||
ev.args.user = { ...u, resourceType: 'User' }
|
||||
})
|
||||
}
|
||||
|
||||
if (oldUser && oldUser.value) {
|
||||
await SystemAPI.oldUserRead({ userID: oldUser.value })
|
||||
.then(u => {
|
||||
ev.args.oldUser = { ...u, resourceType: 'User' }
|
||||
})
|
||||
}
|
||||
|
||||
if (role && role.value) {
|
||||
await SystemAPI.roleRead({ roleID: role.value })
|
||||
.then(r => {
|
||||
ev.args.role = { ...r, resourceType: 'Role' }
|
||||
})
|
||||
}
|
||||
|
||||
if (oldRole && oldRole.value) {
|
||||
await SystemAPI.roleRead({ roleID: oldRole.value })
|
||||
.then(r => {
|
||||
ev.args.oldRole = { ...r, resourceType: 'Role' }
|
||||
})
|
||||
}
|
||||
|
||||
if (application && application.value) {
|
||||
await SystemAPI.applicationRead({ applicationID: application.value })
|
||||
.then(a => {
|
||||
ev.args.application = { ...a }
|
||||
})
|
||||
}
|
||||
|
||||
if (oldApplication && oldApplication.value) {
|
||||
await SystemAPI.applicationRead({ applicationID: oldApplication.value })
|
||||
.then(a => {
|
||||
ev.args.oldApplication = { ...a }
|
||||
})
|
||||
}
|
||||
|
||||
// Add rest to args
|
||||
Object.entries(initialScope).forEach(([key, value]) => {
|
||||
if (!ev.args[key]) {
|
||||
ev.args[key] = {}
|
||||
}
|
||||
})
|
||||
|
||||
return automation.Encode(ev.args)
|
||||
}
|
||||
73
client/web/workflow/src/lib/editor-auto-complete.js
Normal file
@@ -0,0 +1,73 @@
|
||||
export const EXPRESSION_EDITOR_AUTO_COMPLETE_VALUES = [
|
||||
{ value: 'push(', caption: 'push(array, ...elements)' },
|
||||
{ value: 'pop(', caption: 'pop(array)' },
|
||||
{ value: 'shift(', caption: 'shift(array)' },
|
||||
{ value: 'count(', caption: 'count(array, ...elements)' },
|
||||
{ value: 'has(', caption: 'has(arr, ...elements)' },
|
||||
{ value: 'hasAll(', caption: 'hasAll(arr, ...elements)' },
|
||||
{ value: 'find(', caption: 'find(arr, elements)' },
|
||||
{ value: 'sort(', caption: 'sort(array, descending)' },
|
||||
{ value: 'splice(', caption: 'splice(array, start, end)' },
|
||||
{ value: 'min(', caption: 'min(...number)' },
|
||||
{ value: 'max(', caption: 'max(...number)' },
|
||||
{ value: 'round(', caption: 'round(number, places)' },
|
||||
{ value: 'floor(', caption: 'floor(number)' },
|
||||
{ value: 'ceil(', caption: 'ceil(number)' },
|
||||
{ value: 'abs(', caption: 'abs(number)' },
|
||||
{ value: 'log(', caption: 'log(number)' },
|
||||
{ value: 'pow(', caption: 'pow(number, exp)' },
|
||||
{ value: 'sqrt(', caption: 'sqrt(number)' },
|
||||
{ value: 'sum(', caption: 'sum(...number)' },
|
||||
{ value: 'average(', caption: 'average(...number)' },
|
||||
{ value: 'random(', caption: 'random(a, b?)' },
|
||||
{ value: 'int(', caption: 'int(Any)' },
|
||||
{ value: 'float(', caption: 'float(Any)' },
|
||||
{ value: 'toJSON(', caption: 'toJSON(string)' },
|
||||
{ value: 'trim(', caption: 'trim(string)' },
|
||||
{ value: 'trimLeft(', caption: 'trimLeft(string, remove)' },
|
||||
{ value: 'trimRight(', caption: 'trimRight(string, remove)' },
|
||||
{ value: 'toLower(', caption: 'toLower(string)' },
|
||||
{ value: 'toUpper(', caption: 'toUpper(string)' },
|
||||
{ value: 'shortest(', caption: 'shortest(string1, ...strings)' },
|
||||
{ value: 'longest(', caption: 'longest(arg1, arg2, ...arguments)' },
|
||||
{ value: 'format(', caption: 'format(format, ...arguments)' },
|
||||
{ value: 'title(', caption: 'title(string)' },
|
||||
{ value: 'untitle(', caption: 'untitle(string)' },
|
||||
{ value: 'repeat(', caption: 'repeat(string, count)' },
|
||||
{ value: 'replace(', caption: 'replace(string, old, new, count)' },
|
||||
{ value: 'isUrl(', caption: 'isUrl(string)' },
|
||||
{ value: 'isEmail(', caption: 'isEmail(string)' },
|
||||
{ value: 'split(', caption: 'split(string, separator)' },
|
||||
{ value: 'join(', caption: 'join(strings, separator)' },
|
||||
{ value: 'hasSubstring(', caption: 'hasSubstring(string, substring, case)' },
|
||||
{ value: 'substring(', caption: 'substring(string, start, end)' },
|
||||
{ value: 'hasPrefix(', caption: 'hasPrefix(string, prefix)' },
|
||||
{ value: 'hasSuffix(', caption: 'hasSuffix(string, prefix)' },
|
||||
{ value: 'shorten(', caption: 'shorten(string, type, count)' },
|
||||
{ value: 'camelize(', caption: 'camelize(string)' },
|
||||
{ value: 'snakify(', caption: 'snakify(string)' },
|
||||
{ value: 'match(', caption: 'match(string, regex)' },
|
||||
{ value: 'base64encode(', caption: 'base64encode(string)' },
|
||||
{ value: 'set(', caption: 'set(kv, k, v)' },
|
||||
{ value: 'merge(', caption: 'merge(kv, ...kv)' },
|
||||
{ value: 'filter(', caption: 'filter(kv, ...include)' },
|
||||
{ value: 'omit(', caption: 'omit(kv, ...exclude)' },
|
||||
{ value: 'coalesce(', caption: 'coalesce(...Any)' },
|
||||
{ value: 'isEmpty(', caption: 'isEmpty(Any)' },
|
||||
{ value: 'isNil(', caption: 'isNil(Any)' },
|
||||
{ value: 'length(', caption: 'length(array)' },
|
||||
{ value: 'earliest(', caption: 'earliest(DateTime, ...DateTime)' },
|
||||
{ value: 'latest(', caption: 'latest(DateTime, ...DateTime)' },
|
||||
{ value: 'parseISOTime(', caption: 'parseISOTime(string)' },
|
||||
{ value: 'modTime(', caption: 'modTime(datetime, duration)' },
|
||||
{ value: 'modDate(', caption: 'modDate(datetime, days)' },
|
||||
{ value: 'modWeek(', caption: 'modWeek(datetime, weeks)' },
|
||||
{ value: 'modMonth(', caption: 'modMonth(datetime, months)' },
|
||||
{ value: 'modYear(', caption: 'modYear(datetime, years)' },
|
||||
{ value: 'parseDuration(', caption: 'parseDuration(string)' },
|
||||
{ value: 'strftime(', caption: 'strftime(datetime, pattern)' },
|
||||
{ value: 'isLeapYear(', caption: 'isLeapYear(datetime)' },
|
||||
{ value: 'now(', caption: 'now()' },
|
||||
{ value: 'isWeekDay(', caption: 'isWeekDay(datetime)' },
|
||||
{ value: 'sub(', caption: 'sub(from, to)' },
|
||||
]
|
||||
13
client/web/workflow/src/lib/filter.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { filter } from '@cortezaproject/corteza-vue'
|
||||
|
||||
export function objectSearchMaker (field, ...fields) {
|
||||
return function (opts, search) {
|
||||
return opts.filter(o => filter.Assert(o, search, field, ...fields))
|
||||
}
|
||||
}
|
||||
|
||||
export function stringSearchMaker () {
|
||||
return function (opts, search) {
|
||||
return opts.filter(o => filter.Assert(o, search))
|
||||
}
|
||||
}
|
||||
17
client/web/workflow/src/lib/string.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* String utility functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts a camelCase string to Title Case with spaces
|
||||
* @param {string} str - The camelCase string to convert
|
||||
* @returns {string} The converted title case string
|
||||
*/
|
||||
export function camelToTitle (str) {
|
||||
if (!str) return ''
|
||||
|
||||
return str
|
||||
.split(/(?=[A-Z])/)
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
||||
.join(' ')
|
||||
}
|
||||