mirror of
https://github.com/kestra-io/kestra.git
synced 2025-12-19 18:05:41 -05:00
fix(ui) add a better linter configuration
This commit is contained in:
committed by
tchiotludo
parent
367f951900
commit
cd99681712
25
.editorconfig
Normal file
25
.editorconfig
Normal file
@@ -0,0 +1,25 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=lf
|
||||
insert_final_newline=false
|
||||
trim_trailing_whitespace=true
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
continuation_indent_size=4
|
||||
|
||||
[*.yml]
|
||||
indent_size=2
|
||||
|
||||
[*.md]
|
||||
indent_size=2
|
||||
|
||||
[*.yaml]
|
||||
indent_size=2
|
||||
|
||||
[*.json]
|
||||
indent_size=2
|
||||
|
||||
[*.css]
|
||||
indent_size=2
|
||||
@@ -1,5 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
]
|
||||
}
|
||||
presets: ["@vue/app"]
|
||||
};
|
||||
|
||||
@@ -65,10 +65,37 @@
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"plugin:vue/strongly-recommended",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {},
|
||||
"rules": {
|
||||
"vue/html-indent": [
|
||||
"error",
|
||||
4,
|
||||
{
|
||||
"baseIndent": 1
|
||||
}
|
||||
],
|
||||
"vue/script-indent": [
|
||||
"error",
|
||||
4,
|
||||
{
|
||||
"baseIndent": 1
|
||||
}
|
||||
],
|
||||
"vue/max-attributes-per-line": [
|
||||
"error",
|
||||
{
|
||||
"singleline": 7
|
||||
}
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"double"
|
||||
],
|
||||
"vue/object-curly-spacing": ["error", "never"],
|
||||
"object-curly-spacing": ["error", "never"]
|
||||
},
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
}
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
<template>
|
||||
<div>
|
||||
<nprogress-container></nprogress-container>
|
||||
<top-nav-bar :menuCollapsed="menuCollapsed" />
|
||||
<nprogress-container />
|
||||
<top-nav-bar :menu-collapsed="menuCollapsed" />
|
||||
<Menu @onMenuCollapse="onMenuCollapse" />
|
||||
<custom-toast v-if="errorMessage" :noAutoHide="true" toastId="errorToast" :content="errorMessage" :title="$t('error')" />
|
||||
<custom-toast v-if="errorMessage" :no-auto-hide="true" toast-id="errorToast" :content="errorMessage" :title="$t('error')" />
|
||||
<div id="app" class="container-fluid">
|
||||
<div class="content-wrapper" :class="menuCollapsed">
|
||||
<router-view></router-view>
|
||||
<router-view />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Menu from "Override/components/Menu.vue";
|
||||
import TopNavBar from "./components/layout/TopNavBar";
|
||||
import CustomToast from "./components/customToast";
|
||||
import NprogressContainer from "vue-nprogress/src/NprogressContainer";
|
||||
import { mapState } from "vuex";
|
||||
import Menu from "Override/components/Menu.vue";
|
||||
import TopNavBar from "./components/layout/TopNavBar";
|
||||
import CustomToast from "./components/customToast";
|
||||
import NprogressContainer from "vue-nprogress/src/NprogressContainer";
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
name: "app",
|
||||
components: {
|
||||
Menu,
|
||||
TopNavBar,
|
||||
CustomToast,
|
||||
NprogressContainer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
menuCollapsed: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState('core', ['errorMessage'])
|
||||
},
|
||||
created() {
|
||||
if (this.$route.path === "/") {
|
||||
this.$router.push({ name: "flowsList" });
|
||||
}
|
||||
|
||||
this.displayApp()
|
||||
|
||||
this.onMenuCollapse(localStorage.getItem("menuCollapsed") === "true");
|
||||
},
|
||||
methods: {
|
||||
onMenuCollapse(collapse) {
|
||||
this.menuCollapsed = collapse ? "menu-collapsed" : "menu-not-collapsed";
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
Menu,
|
||||
TopNavBar,
|
||||
CustomToast,
|
||||
NprogressContainer
|
||||
},
|
||||
displayApp() {
|
||||
document.getElementById("loader-wrapper").style.display = "none";
|
||||
document.getElementById("app-container").style.display = "block";
|
||||
data() {
|
||||
return {
|
||||
menuCollapsed: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState("core", ["errorMessage"])
|
||||
},
|
||||
created() {
|
||||
if (this.$route.path === "/") {
|
||||
this.$router.push({name: "flowsList"});
|
||||
}
|
||||
|
||||
this.displayApp()
|
||||
|
||||
this.onMenuCollapse(localStorage.getItem("menuCollapsed") === "true");
|
||||
},
|
||||
methods: {
|
||||
onMenuCollapse(collapse) {
|
||||
this.menuCollapsed = collapse ? "menu-collapsed" : "menu-not-collapsed";
|
||||
},
|
||||
displayApp() {
|
||||
document.getElementById("loader-wrapper").style.display = "none";
|
||||
document.getElementById("app-container").style.display = "block";
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
<template>
|
||||
<b-button class="status text-white rounded-lg" :class="'btn-' + cls">
|
||||
<component :is="icon"></component>
|
||||
{{status | lower | cap }}
|
||||
<component :is="icon" />
|
||||
{{ status | lower | cap }}
|
||||
</b-button>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import State from "../utils/state";
|
||||
import PauseCircleOutline from "vue-material-design-icons/PauseCircleOutline";
|
||||
import CheckCircleOutline from "vue-material-design-icons/CheckCircleOutline";
|
||||
import PlayCircleOutline from "vue-material-design-icons/PlayCircleOutline";
|
||||
import CloseCircleOutline from "vue-material-design-icons/CloseCircleOutline";
|
||||
import StopCircleOutline from "vue-material-design-icons/StopCircleOutline";
|
||||
import State from "../utils/state";
|
||||
import PauseCircleOutline from "vue-material-design-icons/PauseCircleOutline";
|
||||
import CheckCircleOutline from "vue-material-design-icons/CheckCircleOutline";
|
||||
import PlayCircleOutline from "vue-material-design-icons/PlayCircleOutline";
|
||||
import CloseCircleOutline from "vue-material-design-icons/CloseCircleOutline";
|
||||
import StopCircleOutline from "vue-material-design-icons/StopCircleOutline";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PauseCircleOutline,
|
||||
CheckCircleOutline,
|
||||
PlayCircleOutline,
|
||||
CloseCircleOutline,
|
||||
StopCircleOutline
|
||||
},
|
||||
props: {
|
||||
status: {
|
||||
type: String,
|
||||
required: true
|
||||
export default {
|
||||
components: {
|
||||
PauseCircleOutline,
|
||||
CheckCircleOutline,
|
||||
PlayCircleOutline,
|
||||
CloseCircleOutline,
|
||||
StopCircleOutline
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
cls() {
|
||||
return State.colorClass()[this.status] + (this.size ? " btn-" + this.size : "");
|
||||
props: {
|
||||
status: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
icon () {
|
||||
return State.icon()[this.status];
|
||||
computed: {
|
||||
cls() {
|
||||
return State.colorClass()[this.status] + (this.size ? " btn-" + this.size : "");
|
||||
},
|
||||
icon () {
|
||||
return State.icon()[this.status];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
button.status {
|
||||
|
||||
@@ -2,55 +2,62 @@
|
||||
<b-toast @hide="onHide" :id="toastId" :variant="variant" solid :no-auto-hide="noAutoHide">
|
||||
<template v-slot:toast-title>
|
||||
<div class="d-flex flex-grow-1 align-items-baseline">
|
||||
<strong class="mr-auto">{{title}}</strong>
|
||||
<strong class="mr-auto">{{ title }}</strong>
|
||||
</div>
|
||||
</template>
|
||||
<span>{{content.message || content}}</span>
|
||||
<b-table class="mt-2 mb-0" small bordered v-if="items && items.length > 0" striped hover
|
||||
:items="items"></b-table>
|
||||
<span>{{ content.message || content }}</span>
|
||||
<b-table
|
||||
class="mt-2 mb-0"
|
||||
small
|
||||
bordered
|
||||
v-if="items && items.length > 0"
|
||||
striped
|
||||
hover
|
||||
:items="items"
|
||||
/>
|
||||
</b-toast>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
variant: {
|
||||
type: String,
|
||||
default: "danger"
|
||||
export default {
|
||||
props: {
|
||||
variant: {
|
||||
type: String,
|
||||
default: "danger"
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
toastId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
content: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
noAutoHide: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
mounted() {
|
||||
this.$bvToast.show(this.toastId);
|
||||
},
|
||||
toastId: {
|
||||
type: String,
|
||||
required: true
|
||||
computed: {
|
||||
items() {
|
||||
const messages = this.content && this.content._embedded && this.content._embedded.errors ? this.content._embedded.errors : []
|
||||
return Array.isArray(messages) ? messages : [messages]
|
||||
}
|
||||
},
|
||||
content: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
noAutoHide: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
methods: {
|
||||
onHide() {
|
||||
setTimeout(() => {
|
||||
this.$store.commit("core/setErrorMessage", undefined);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$bvToast.show(this.toastId);
|
||||
},
|
||||
computed: {
|
||||
items() {
|
||||
const messages = this.content && this.content._embedded && this.content._embedded.errors ? this.content._embedded.errors : []
|
||||
return Array.isArray(messages) ? messages : [messages]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onHide() {
|
||||
setTimeout(() => {
|
||||
this.$store.commit("core/setErrorMessage", undefined);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import "../styles/variable";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div v-if="execution && outputs">
|
||||
<b-navbar toggleable="lg" type="light" variant="light">
|
||||
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
|
||||
<b-navbar-toggle target="nav-collapse" />
|
||||
<b-collapse id="nav-collapse" is-nav>
|
||||
<b-nav-form>
|
||||
<v-select
|
||||
@@ -10,7 +10,7 @@
|
||||
@input="onSearch"
|
||||
:options="selectOptions"
|
||||
:placeholder="$t('display output for specific task') + '...'"
|
||||
></v-select>
|
||||
/>
|
||||
</b-nav-form>
|
||||
</b-collapse>
|
||||
</b-navbar>
|
||||
@@ -39,96 +39,96 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import md5 from "md5";
|
||||
import VarValue from "./VarValue";
|
||||
import Utils from "../../utils/utils";
|
||||
import {mapState} from "vuex";
|
||||
import md5 from "md5";
|
||||
import VarValue from "./VarValue";
|
||||
import Utils from "../../utils/utils";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VarValue,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filter: ""
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.search) {
|
||||
this.filter = this.$route.query.search || ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
if (this.$route.query.search !== this.filter) {
|
||||
this.filter = this.$route.query.search || "";
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onSearch() {
|
||||
if (this.filter && this.$route.query.search !== this.filter) {
|
||||
const newRoute = { query: { ...this.$route.query } };
|
||||
newRoute.query.search = this.filter;
|
||||
this.$router.push(newRoute);
|
||||
export default {
|
||||
components: {
|
||||
VarValue,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filter: ""
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.search) {
|
||||
this.filter = this.$route.query.search || ""
|
||||
}
|
||||
},
|
||||
taskRunOutputToken(taskRun) {
|
||||
return md5(taskRun.taskId + (taskRun.value ? ` - ${taskRun.value}`: ''));
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("execution", ["execution"]),
|
||||
selectOptions() {
|
||||
const options = {};
|
||||
for (const taskRun of this.execution.taskRunList || []) {
|
||||
options[this.taskRunOutputToken(taskRun)] = {
|
||||
label: taskRun.taskId + (taskRun.value ? ` - ${taskRun.value}`: ''),
|
||||
value: this.taskRunOutputToken(taskRun)
|
||||
watch: {
|
||||
$route() {
|
||||
if (this.$route.query.search !== this.filter) {
|
||||
this.filter = this.$route.query.search || "";
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(options);
|
||||
},
|
||||
fields() {
|
||||
return [
|
||||
{
|
||||
key: "task",
|
||||
label: this.$t("task")
|
||||
},
|
||||
{
|
||||
key: "value",
|
||||
label: this.$t("value")
|
||||
},
|
||||
{
|
||||
key: "key",
|
||||
label: this.$t("name")
|
||||
},
|
||||
{
|
||||
key: "output",
|
||||
label: this.$t("output")
|
||||
}
|
||||
];
|
||||
},
|
||||
outputs() {
|
||||
const outputs = [];
|
||||
for (const taskRun of this.execution.taskRunList || []) {
|
||||
const token = this.taskRunOutputToken(taskRun)
|
||||
if (!this.filter || token === this.filter) {
|
||||
Utils.executionVars(taskRun.outputs).forEach(output => {
|
||||
const item = {
|
||||
key: output.key,
|
||||
output: output.value,
|
||||
task: taskRun.taskId,
|
||||
value: taskRun.value
|
||||
};
|
||||
|
||||
outputs.push(item);
|
||||
})
|
||||
methods: {
|
||||
onSearch() {
|
||||
if (this.filter && this.$route.query.search !== this.filter) {
|
||||
const newRoute = {query: {...this.$route.query}};
|
||||
newRoute.query.search = this.filter;
|
||||
this.$router.push(newRoute);
|
||||
}
|
||||
},
|
||||
taskRunOutputToken(taskRun) {
|
||||
return md5(taskRun.taskId + (taskRun.value ? ` - ${taskRun.value}`: ""));
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("execution", ["execution"]),
|
||||
selectOptions() {
|
||||
const options = {};
|
||||
for (const taskRun of this.execution.taskRunList || []) {
|
||||
options[this.taskRunOutputToken(taskRun)] = {
|
||||
label: taskRun.taskId + (taskRun.value ? ` - ${taskRun.value}`: ""),
|
||||
value: this.taskRunOutputToken(taskRun)
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(options);
|
||||
},
|
||||
fields() {
|
||||
return [
|
||||
{
|
||||
key: "task",
|
||||
label: this.$t("task")
|
||||
},
|
||||
{
|
||||
key: "value",
|
||||
label: this.$t("value")
|
||||
},
|
||||
{
|
||||
key: "key",
|
||||
label: this.$t("name")
|
||||
},
|
||||
{
|
||||
key: "output",
|
||||
label: this.$t("output")
|
||||
}
|
||||
];
|
||||
},
|
||||
outputs() {
|
||||
const outputs = [];
|
||||
for (const taskRun of this.execution.taskRunList || []) {
|
||||
const token = this.taskRunOutputToken(taskRun)
|
||||
if (!this.filter || token === this.filter) {
|
||||
Utils.executionVars(taskRun.outputs).forEach(output => {
|
||||
const item = {
|
||||
key: output.key,
|
||||
output: output.value,
|
||||
task: taskRun.taskId,
|
||||
value: taskRun.value
|
||||
};
|
||||
|
||||
outputs.push(item);
|
||||
})
|
||||
}
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -17,148 +17,148 @@
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import Gantt from "./Gantt";
|
||||
import Overview from "./Overview";
|
||||
import Logs from "../logs/Logs";
|
||||
import Topology from "./Topology";
|
||||
import ExecutionOutput from "./ExecutionOutput";
|
||||
import Trigger from "vue-material-design-icons/Cogs";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import FlowActions from "../flows/FlowActions";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import { mapState } from "vuex";
|
||||
import Gantt from "./Gantt";
|
||||
import Overview from "./Overview";
|
||||
import Logs from "../logs/Logs";
|
||||
import Topology from "./Topology";
|
||||
import ExecutionOutput from "./ExecutionOutput";
|
||||
import Trigger from "vue-material-design-icons/Cogs";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import FlowActions from "../flows/FlowActions";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
components: {
|
||||
Overview,
|
||||
BottomLine,
|
||||
Trigger,
|
||||
Gantt,
|
||||
Logs,
|
||||
Topology,
|
||||
FlowActions,
|
||||
ExecutionOutput
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sse: undefined
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.follow();
|
||||
},
|
||||
methods: {
|
||||
follow() {
|
||||
this.closeSSE();
|
||||
|
||||
this.$store
|
||||
.dispatch("execution/followExecution", this.$route.params)
|
||||
.then(sse => {
|
||||
this.sse = sse;
|
||||
sse.subscribe("", (data, event) => {
|
||||
this.$store.commit("execution/setExecution", data);
|
||||
if (this.$route.query.tab === 'topology') {
|
||||
this.$store.dispatch('execution/loadTree', data)
|
||||
}
|
||||
if (event && event.lastEventId === "end") {
|
||||
this.closeSSE();
|
||||
}
|
||||
});
|
||||
});
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
components: {
|
||||
Overview,
|
||||
BottomLine,
|
||||
Trigger,
|
||||
Gantt,
|
||||
Logs,
|
||||
Topology,
|
||||
FlowActions,
|
||||
ExecutionOutput
|
||||
},
|
||||
closeSSE() {
|
||||
if (this.sse) {
|
||||
this.sse.close();
|
||||
this.sse = undefined;
|
||||
}
|
||||
},
|
||||
setTab(tab) {
|
||||
this.$store.commit("execution/setTask", undefined);
|
||||
this.$router.push({
|
||||
name: "executionEdit",
|
||||
params: this.$route.params,
|
||||
query: { tab }
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("execution", ["execution"]),
|
||||
routeInfo() {
|
||||
const ns = this.$route.params.namespace;
|
||||
data() {
|
||||
return {
|
||||
title: this.$t("execution"),
|
||||
breadcrumb: [
|
||||
{
|
||||
label: this.$t("flows"),
|
||||
link: {
|
||||
name: "flowsList",
|
||||
query: {
|
||||
namespace: ns
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: `${ns}.${this.$route.params.flowId}`,
|
||||
link: {
|
||||
name: "flowEdit",
|
||||
params: {
|
||||
namespace: ns,
|
||||
id: this.$route.params.flowId
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t("executions"),
|
||||
link: {
|
||||
name: 'flowEdit',
|
||||
params: {
|
||||
namespace: ns,
|
||||
id: this.$route.params.flowId
|
||||
},
|
||||
query: {
|
||||
tab: 'executions'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$route.params.id,
|
||||
link: {
|
||||
name: "executionEdit"
|
||||
}
|
||||
}
|
||||
]
|
||||
sse: undefined
|
||||
};
|
||||
},
|
||||
tabs() {
|
||||
const title = title => this.$t(title);
|
||||
return [
|
||||
{
|
||||
tab: "overview",
|
||||
title: title("overview")
|
||||
},
|
||||
{
|
||||
tab: "gantt",
|
||||
title: title("gantt")
|
||||
},
|
||||
{
|
||||
tab: "logs",
|
||||
title: title("logs")
|
||||
},
|
||||
{
|
||||
tab: "topology",
|
||||
title: title("topology")
|
||||
},
|
||||
{
|
||||
tab: "execution-output",
|
||||
title: title("output")
|
||||
created() {
|
||||
this.follow();
|
||||
},
|
||||
methods: {
|
||||
follow() {
|
||||
this.closeSSE();
|
||||
|
||||
this.$store
|
||||
.dispatch("execution/followExecution", this.$route.params)
|
||||
.then(sse => {
|
||||
this.sse = sse;
|
||||
sse.subscribe("", (data, event) => {
|
||||
this.$store.commit("execution/setExecution", data);
|
||||
if (this.$route.query.tab === "topology") {
|
||||
this.$store.dispatch("execution/loadTree", data)
|
||||
}
|
||||
if (event && event.lastEventId === "end") {
|
||||
this.closeSSE();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
closeSSE() {
|
||||
if (this.sse) {
|
||||
this.sse.close();
|
||||
this.sse = undefined;
|
||||
}
|
||||
];
|
||||
},
|
||||
setTab(tab) {
|
||||
this.$store.commit("execution/setTask", undefined);
|
||||
this.$router.push({
|
||||
name: "executionEdit",
|
||||
params: this.$route.params,
|
||||
query: {tab}
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("execution", ["execution"]),
|
||||
routeInfo() {
|
||||
const ns = this.$route.params.namespace;
|
||||
return {
|
||||
title: this.$t("execution"),
|
||||
breadcrumb: [
|
||||
{
|
||||
label: this.$t("flows"),
|
||||
link: {
|
||||
name: "flowsList",
|
||||
query: {
|
||||
namespace: ns
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: `${ns}.${this.$route.params.flowId}`,
|
||||
link: {
|
||||
name: "flowEdit",
|
||||
params: {
|
||||
namespace: ns,
|
||||
id: this.$route.params.flowId
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$t("executions"),
|
||||
link: {
|
||||
name: "flowEdit",
|
||||
params: {
|
||||
namespace: ns,
|
||||
id: this.$route.params.flowId
|
||||
},
|
||||
query: {
|
||||
tab: "executions"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$route.params.id,
|
||||
link: {
|
||||
name: "executionEdit"
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
tabs() {
|
||||
const title = title => this.$t(title);
|
||||
return [
|
||||
{
|
||||
tab: "overview",
|
||||
title: title("overview")
|
||||
},
|
||||
{
|
||||
tab: "gantt",
|
||||
title: title("gantt")
|
||||
},
|
||||
{
|
||||
tab: "logs",
|
||||
title: title("logs")
|
||||
},
|
||||
{
|
||||
tab: "topology",
|
||||
title: title("topology")
|
||||
},
|
||||
{
|
||||
tab: "execution-output",
|
||||
title: title("output")
|
||||
}
|
||||
];
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.closeSSE();
|
||||
this.$store.commit("execution/setExecution", undefined);
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.closeSSE();
|
||||
this.$store.commit("execution/setExecution", undefined);
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<data-table @onPageChanged="onPageChanged" ref="dataTable" :total="total">
|
||||
<template v-slot:navbar>
|
||||
<search-field ref="searchField" @onSearch="onSearch" :fields="searchableFields" />
|
||||
<namespace-select data-type="flow" v-if="$route.name !== 'flowEdit'" @onNamespaceSelect="onNamespaceSelect" />
|
||||
<status-filter-buttons @onRefresh="onStatusChange"/>
|
||||
<namespace-select data-type="flow" v-if="$route.name !== 'flowEdit'" @onNamespaceSelect="onNamespaceSelect" />
|
||||
<status-filter-buttons @onRefresh="onStatusChange" />
|
||||
<date-range @onDate="onSearch" />
|
||||
<refresh-button class="float-right" @onRefresh="loadData"/>
|
||||
<refresh-button class="float-right" @onRefresh="loadData" />
|
||||
</template>
|
||||
|
||||
<template v-slot:top>
|
||||
@@ -30,7 +30,7 @@
|
||||
@row-dblclicked="onRowDoubleClick"
|
||||
>
|
||||
<template #empty>
|
||||
<span class="text-black-50">{{$t('no result')}}</span>
|
||||
<span class="text-black-50">{{ $t('no result') }}</span>
|
||||
</template>
|
||||
|
||||
<template v-slot:cell(details)="row">
|
||||
@@ -40,13 +40,15 @@
|
||||
</template>
|
||||
<template
|
||||
v-slot:cell(state.startDate)="row"
|
||||
>{{row.item.state.startDate | date('LLLL')}}</template>
|
||||
>
|
||||
{{ row.item.state.startDate | date('LLLL') }}
|
||||
</template>
|
||||
<template
|
||||
v-slot:cell(state.endDate)="row"
|
||||
>
|
||||
<span v-if="!['RUNNING', 'CREATED'].includes(row.item.state.current)">
|
||||
{{row.item.state.endDate | date('LLLL')}}
|
||||
</span>
|
||||
<span v-if="!['RUNNING', 'CREATED'].includes(row.item.state.current)">
|
||||
{{ row.item.state.endDate | date('LLLL') }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-slot:cell(state.current)="row">
|
||||
<status
|
||||
@@ -55,197 +57,197 @@
|
||||
size="sm"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:cell(state.duration)="row">
|
||||
<span v-if="['RUNNING', 'CREATED'].includes(row.item.state.current)">{{durationFrom(row.item) | humanizeDuration}}</span>
|
||||
<span v-else>{{row.item.state.duration | humanizeDuration}}</span>
|
||||
<template v-slot:cell(state.duration)="row">
|
||||
<span v-if="['RUNNING', 'CREATED'].includes(row.item.state.current)">{{ durationFrom(row.item) | humanizeDuration }}</span>
|
||||
<span v-else>{{ row.item.state.duration | humanizeDuration }}</span>
|
||||
</template>
|
||||
<template v-slot:cell(flowId)="row">
|
||||
<router-link
|
||||
:to="{name: 'flowEdit', params: {namespace: row.item.namespace, id: row.item.flowId}}"
|
||||
>{{row.item.flowId}}</router-link>
|
||||
>
|
||||
{{ row.item.flowId }}
|
||||
</router-link>
|
||||
</template>
|
||||
<template v-slot:cell(id)="row">
|
||||
<code>{{row.item.id | id}}</code>
|
||||
<code>{{ row.item.id | id }}</code>
|
||||
</template>
|
||||
|
||||
<template v-slot:cell(trigger)="row">
|
||||
<trigger-avatar @showTriggerDetails="showTriggerDetails" :execution="row.item"/>
|
||||
<trigger-avatar @showTriggerDetails="showTriggerDetails" :execution="row.item" />
|
||||
</template>
|
||||
|
||||
</b-table>
|
||||
</template>
|
||||
</data-table>
|
||||
|
||||
<flow-trigger-details-modal v-if="flowTriggerDetails" :trigger="flowTriggerDetails"/>
|
||||
|
||||
<flow-trigger-details-modal v-if="flowTriggerDetails" :trigger="flowTriggerDetails" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import DataTable from "../layout/DataTable";
|
||||
import Eye from "vue-material-design-icons/Eye";
|
||||
import Status from "../Status";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import DataTableActions from "../../mixins/dataTableActions";
|
||||
import SearchField from "../layout/SearchField";
|
||||
import NamespaceSelect from "../namespace/NamespaceSelect";
|
||||
import DateRange from "../layout/DateRange";
|
||||
import RefreshButton from '../layout/RefreshButton'
|
||||
import StatusFilterButtons from '../layout/StatusFilterButtons'
|
||||
import StateGlobalChart from "../../components/stats/StateGlobalChart";
|
||||
import FlowTriggerDetailsModal from "../../components/flows/TriggerDetailsModal";
|
||||
import TriggerAvatar from "../../components/flows/TriggerAvatar";
|
||||
import {mapState} from "vuex";
|
||||
import DataTable from "../layout/DataTable";
|
||||
import Eye from "vue-material-design-icons/Eye";
|
||||
import Status from "../Status";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import DataTableActions from "../../mixins/dataTableActions";
|
||||
import SearchField from "../layout/SearchField";
|
||||
import NamespaceSelect from "../namespace/NamespaceSelect";
|
||||
import DateRange from "../layout/DateRange";
|
||||
import RefreshButton from "../layout/RefreshButton"
|
||||
import StatusFilterButtons from "../layout/StatusFilterButtons"
|
||||
import StateGlobalChart from "../../components/stats/StateGlobalChart";
|
||||
import FlowTriggerDetailsModal from "../../components/flows/TriggerDetailsModal";
|
||||
import TriggerAvatar from "../../components/flows/TriggerAvatar";
|
||||
|
||||
export default {
|
||||
mixins: [RouteContext, DataTableActions],
|
||||
components: {
|
||||
Status,
|
||||
Eye,
|
||||
DataTable,
|
||||
SearchField,
|
||||
NamespaceSelect,
|
||||
DateRange,
|
||||
RefreshButton,
|
||||
StatusFilterButtons,
|
||||
StateGlobalChart,
|
||||
FlowTriggerDetailsModal,
|
||||
TriggerAvatar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataType: "execution",
|
||||
dailyReady: false,
|
||||
flowTriggerDetails: undefined
|
||||
};
|
||||
},
|
||||
beforeCreate() {
|
||||
const q = JSON.parse(localStorage.getItem('executionQueries') || '{}')
|
||||
q.sort = q.sort ? q.sort : 'state.startDate:desc'
|
||||
q.status = q.status ? q.status : 'ALL'
|
||||
localStorage.setItem('executionQueries', JSON.stringify(q))
|
||||
},
|
||||
computed: {
|
||||
...mapState("execution", ["executions", "total"]),
|
||||
...mapState("stat", ["daily"]),
|
||||
fields() {
|
||||
const title = title => {
|
||||
return this.$t(title);
|
||||
export default {
|
||||
mixins: [RouteContext, DataTableActions],
|
||||
components: {
|
||||
Status,
|
||||
Eye,
|
||||
DataTable,
|
||||
SearchField,
|
||||
NamespaceSelect,
|
||||
DateRange,
|
||||
RefreshButton,
|
||||
StatusFilterButtons,
|
||||
StateGlobalChart,
|
||||
FlowTriggerDetailsModal,
|
||||
TriggerAvatar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataType: "execution",
|
||||
dailyReady: false,
|
||||
flowTriggerDetails: undefined
|
||||
};
|
||||
return [
|
||||
{
|
||||
key: "id",
|
||||
label: title("id")
|
||||
},
|
||||
{
|
||||
key: "state.startDate",
|
||||
label: title("start date"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "state.endDate",
|
||||
label: title("end date"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "state.duration",
|
||||
label: title("duration"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "namespace",
|
||||
label: title("namespace"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "flowId",
|
||||
label: title("flow"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "state.current",
|
||||
label: title("state"),
|
||||
class: "text-center",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "trigger",
|
||||
label: title("trigger"),
|
||||
class: "shrink"
|
||||
},
|
||||
{
|
||||
key: "details",
|
||||
label: "",
|
||||
class: "row-action"
|
||||
},
|
||||
beforeCreate() {
|
||||
const q = JSON.parse(localStorage.getItem("executionQueries") || "{}")
|
||||
q.sort = q.sort ? q.sort : "state.startDate:desc"
|
||||
q.status = q.status ? q.status : "ALL"
|
||||
localStorage.setItem("executionQueries", JSON.stringify(q))
|
||||
},
|
||||
computed: {
|
||||
...mapState("execution", ["executions", "total"]),
|
||||
...mapState("stat", ["daily"]),
|
||||
fields() {
|
||||
const title = title => {
|
||||
return this.$t(title);
|
||||
};
|
||||
return [
|
||||
{
|
||||
key: "id",
|
||||
label: title("id")
|
||||
},
|
||||
{
|
||||
key: "state.startDate",
|
||||
label: title("start date"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "state.endDate",
|
||||
label: title("end date"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "state.duration",
|
||||
label: title("duration"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "namespace",
|
||||
label: title("namespace"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "flowId",
|
||||
label: title("flow"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "state.current",
|
||||
label: title("state"),
|
||||
class: "text-center",
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "trigger",
|
||||
label: title("trigger"),
|
||||
class: "shrink"
|
||||
},
|
||||
{
|
||||
key: "details",
|
||||
label: "",
|
||||
class: "row-action"
|
||||
}
|
||||
];
|
||||
},
|
||||
executionQuery() {
|
||||
let filter;
|
||||
|
||||
if (this.$route.name === "flowEdit") {
|
||||
filter = `namespace:${this.$route.params.namespace} AND flowId:${this.$route.params.id}`;
|
||||
}
|
||||
];
|
||||
},
|
||||
executionQuery() {
|
||||
let filter;
|
||||
|
||||
if (this.$route.name === "flowEdit") {
|
||||
filter = `namespace:${this.$route.params.namespace} AND flowId:${this.$route.params.id}`;
|
||||
return this.query + (filter ? " " + filter : "");
|
||||
},
|
||||
endDate() {
|
||||
return new Date();
|
||||
},
|
||||
startDate() {
|
||||
return this.$moment(this.endDate)
|
||||
.add(-30, "days")
|
||||
.toDate();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onStatusChange() {
|
||||
this.saveFilters()
|
||||
this.loadData()
|
||||
},
|
||||
showTriggerDetails(trigger) {
|
||||
this.flowTriggerDetails = trigger
|
||||
this.$bvModal.show("modal-triggers-details")
|
||||
},
|
||||
triggerExecution() {
|
||||
this.$store
|
||||
.dispatch("execution/triggerExecution", this.$route.params)
|
||||
.then(response => {
|
||||
this.$router.push({
|
||||
name: "execution",
|
||||
params: response.data
|
||||
});
|
||||
|
||||
return this.query + (filter ? " " + filter : "");
|
||||
},
|
||||
endDate() {
|
||||
return new Date();
|
||||
},
|
||||
startDate() {
|
||||
return this.$moment(this.endDate)
|
||||
.add(-30, "days")
|
||||
.toDate();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onStatusChange() {
|
||||
this.saveFilters()
|
||||
this.loadData()
|
||||
},
|
||||
showTriggerDetails(trigger) {
|
||||
this.flowTriggerDetails = trigger
|
||||
this.$bvModal.show('modal-triggers-details')
|
||||
},
|
||||
triggerExecution() {
|
||||
this.$store
|
||||
.dispatch("execution/triggerExecution", this.$route.params)
|
||||
.then(response => {
|
||||
this.$router.push({
|
||||
name: "execution",
|
||||
params: response.data
|
||||
return response.data
|
||||
})
|
||||
.then((execution) => {
|
||||
this.$toast().success(this.$t("triggered done", {name: execution.id}));
|
||||
})
|
||||
},
|
||||
loadData(callback) {
|
||||
this.dailyReady = false;
|
||||
this.$store
|
||||
.dispatch("stat/daily", {
|
||||
q: this.executionQuery,
|
||||
startDate: this.$moment(this.startDate).format("YYYY-MM-DD"),
|
||||
endDate: this.$moment(this.endDate).format("YYYY-MM-DD")
|
||||
})
|
||||
.then(() => {
|
||||
this.dailyReady = true;
|
||||
});
|
||||
|
||||
return response.data
|
||||
})
|
||||
.then((execution) => {
|
||||
this.$toast().success(this.$t('triggered done', {name: execution.id}));
|
||||
})
|
||||
},
|
||||
loadData(callback) {
|
||||
this.dailyReady = false;
|
||||
this.$store
|
||||
.dispatch("stat/daily", {
|
||||
|
||||
this.$store.dispatch("execution/findExecutions", {
|
||||
size: parseInt(this.$route.query.size || 25),
|
||||
page: parseInt(this.$route.query.page || 1),
|
||||
q: this.executionQuery,
|
||||
startDate: this.$moment(this.startDate).format('YYYY-MM-DD'),
|
||||
endDate: this.$moment(this.endDate).format('YYYY-MM-DD')
|
||||
})
|
||||
.then(() => {
|
||||
this.dailyReady = true;
|
||||
});
|
||||
|
||||
|
||||
this.$store.dispatch("execution/findExecutions", {
|
||||
size: parseInt(this.$route.query.size || 25),
|
||||
page: parseInt(this.$route.query.page || 1),
|
||||
q: this.executionQuery,
|
||||
sort: this.$route.query.sort,
|
||||
state: this.$route.query.status
|
||||
}).finally(callback);
|
||||
},
|
||||
durationFrom(item) {
|
||||
return (+new Date() - new Date(item.state.startDate).getTime()) / 1000
|
||||
},
|
||||
}
|
||||
};
|
||||
sort: this.$route.query.sort,
|
||||
state: this.$route.query.status
|
||||
}).finally(callback);
|
||||
},
|
||||
durationFrom(item) {
|
||||
return (+new Date() - new Date(item.state.startDate).getTime()) / 1000
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<thead>
|
||||
<tr class="bg-light">
|
||||
<th>{{ duration }}</th>
|
||||
<td v-for="(date, i) in dates" :key="i">{{ date }}</td>
|
||||
<td v-for="(date, i) in dates" :key="i">
|
||||
{{ date }}
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-for="taskItem in series" :key="taskItem.id">
|
||||
@@ -18,13 +20,15 @@
|
||||
:target="`task-title-wrapper-${taskItem.id}`"
|
||||
>
|
||||
<code>{{ taskItem.name }}</code>
|
||||
<span v-if="taskItem.task && taskItem.task.value"><br/>{{ taskItem.task.value }}</span>
|
||||
<span v-if="taskItem.task && taskItem.task.value"><br>{{ taskItem.task.value }}</span>
|
||||
</b-tooltip>
|
||||
</th>
|
||||
<td :colspan="dates.length">
|
||||
<b-tooltip
|
||||
:target="`task-progress-${taskItem.id}`"
|
||||
placement="left"><span v-html="taskItem.tooltip"></span>
|
||||
placement="left"
|
||||
>
|
||||
<span v-html="taskItem.tooltip" />
|
||||
</b-tooltip>
|
||||
<div
|
||||
:style="{left: Math.max(1, (taskItem.start - 1)) + '%', width: taskItem.width - 1 + '%'}"
|
||||
@@ -33,18 +37,19 @@
|
||||
:id="`task-progress-${taskItem.id}`"
|
||||
>
|
||||
<div class="progress">
|
||||
<div class="progress-bar"
|
||||
:style="{left: taskItem.left + '%', width: (100-taskItem.left) + '%'}"
|
||||
:class="'bg-' + taskItem.color + (taskItem.running ? ' progress-bar-striped' : '')"
|
||||
role="progressbar">
|
||||
</div>
|
||||
<div
|
||||
class="progress-bar"
|
||||
:style="{left: taskItem.left + '%', width: (100-taskItem.left) + '%'}"
|
||||
:class="'bg-' + taskItem.color + (taskItem.running ? ' progress-bar-striped' : '')"
|
||||
role="progressbar"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="task && task.id === taskItem.id">
|
||||
<td :colspan="dates.length + 1">
|
||||
<log-list :task-run-id="task.id" level="TRACE"/>
|
||||
<log-list :task-run-id="task.id" level="TRACE" />
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<div v-if="execution">
|
||||
<b-row class="mb-3 text-right">
|
||||
<b-col>
|
||||
<restart :execution="execution" @restart="restart"/>
|
||||
<restart :execution="execution" @restart="restart" />
|
||||
<kill :execution="execution" />
|
||||
<status :status="execution.state.current"/>
|
||||
<status :status="execution.state.current" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-table responsive="xl" striped hover bordered :items="items" class="mb-0">
|
||||
@@ -12,7 +12,8 @@
|
||||
<router-link
|
||||
v-if="row.item.link"
|
||||
:to="{name: 'executionEdit', params: row.item.link}"
|
||||
>{{ row.item.value }}
|
||||
>
|
||||
{{ row.item.value }}
|
||||
</router-link>
|
||||
<span v-else>
|
||||
<span v-if="row.item.key === $t('revision')">
|
||||
@@ -27,17 +28,17 @@
|
||||
|
||||
<div v-if="execution.trigger" class="mt-4">
|
||||
<h5>{{ $t('trigger') }}</h5>
|
||||
<vars :execution="execution" :data="execution.trigger"/>
|
||||
<vars :execution="execution" :data="execution.trigger" />
|
||||
</div>
|
||||
|
||||
<div v-if="execution.inputs" class="mt-4">
|
||||
<h5>{{ $t('inputs') }}</h5>
|
||||
<vars :execution="execution" :data="inputs"/>
|
||||
<vars :execution="execution" :data="inputs" />
|
||||
</div>
|
||||
|
||||
<div v-if="execution.variables" class="mt-4">
|
||||
<h5>{{ $t('variables') }}</h5>
|
||||
<vars :execution="execution" :data="execution.variables"/>
|
||||
<vars :execution="execution" :data="execution.variables" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -78,8 +79,8 @@
|
||||
items() {
|
||||
const startTs = this.execution.state.histories[0].date;
|
||||
const stopTs = this.execution.state.histories[
|
||||
this.execution.state.histories.length - 1
|
||||
].date;
|
||||
this.execution.state.histories.length - 1
|
||||
].date;
|
||||
const delta = ts(stopTs) - ts(startTs);
|
||||
const duration = this.$moment.duration(delta);
|
||||
const humanDuration = humanizeDuration(duration);
|
||||
@@ -94,8 +95,8 @@
|
||||
key: this.$t("revision"),
|
||||
value: this.execution.flowRevision
|
||||
},
|
||||
{key: this.$t("created date"), value: this.$moment(startTs).format('LLLL')},
|
||||
{key: this.$t("updated date"), value: this.$moment(stopTs).format('LLLL')},
|
||||
{key: this.$t("created date"), value: this.$moment(startTs).format("LLLL")},
|
||||
{key: this.$t("updated date"), value: this.$moment(stopTs).format("LLLL")},
|
||||
{key: this.$t("duration"), value: humanDuration},
|
||||
{key: this.$t("steps"), value: stepCount}
|
||||
];
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<span>
|
||||
<b-button
|
||||
@click="restart"
|
||||
v-if="enabled"
|
||||
:class="!isButtonGroup ? 'rounded-lg btn-info restart mr-1' : ''"
|
||||
:title="$t('restart')"
|
||||
>
|
||||
<restart-icon/>
|
||||
{{ (isButtonGroup ? '' : $t("restart")) }}
|
||||
</b-button>
|
||||
</span>
|
||||
<span>
|
||||
<b-button
|
||||
@click="restart"
|
||||
v-if="enabled"
|
||||
:class="!isButtonGroup ? 'rounded-lg btn-info restart mr-1' : ''"
|
||||
:title="$t('restart')"
|
||||
>
|
||||
<restart-icon />
|
||||
{{ (isButtonGroup ? '' : $t("restart")) }}
|
||||
</b-button>
|
||||
</span>
|
||||
</template>
|
||||
<script>
|
||||
import RestartIcon from "vue-material-design-icons/Restart";
|
||||
@@ -30,7 +30,8 @@
|
||||
},
|
||||
task: {
|
||||
type: Object,
|
||||
required: false
|
||||
required: false,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -43,9 +44,9 @@
|
||||
taskId: this.task ? this.task.taskId : null
|
||||
})
|
||||
.then(response => {
|
||||
this.$store.commit('execution/setExecution', response.data);
|
||||
this.$router.push({name: 'executionEdit', params: response.data});
|
||||
this.$emit('restart')
|
||||
this.$store.commit("execution/setExecution", response.data);
|
||||
this.$router.push({name: "executionEdit", params: response.data});
|
||||
this.$emit("restart")
|
||||
})
|
||||
.then(() => {
|
||||
this.$toast().success(this.$t("restarted"));
|
||||
|
||||
@@ -4,36 +4,36 @@
|
||||
<topology-tree
|
||||
ref="topology"
|
||||
v-if="execution && dataTree"
|
||||
:dataTree="dataTree"
|
||||
:data-tree="dataTree"
|
||||
:label="getLabel"
|
||||
/>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</template>
|
||||
<script>
|
||||
import TopologyTree from "../graph/TopologyTree";
|
||||
import { mapState } from "vuex";
|
||||
export default {
|
||||
components: {
|
||||
TopologyTree
|
||||
},
|
||||
computed: {
|
||||
...mapState("execution", ["execution", "dataTree"])
|
||||
},
|
||||
created() {
|
||||
if (!this.dataTree && this.execution) {
|
||||
this.$store.dispatch('execution/loadTree', this.execution)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getLabel(node) {
|
||||
return node.data.taskId;
|
||||
import TopologyTree from "../graph/TopologyTree";
|
||||
import {mapState} from "vuex";
|
||||
export default {
|
||||
components: {
|
||||
TopologyTree
|
||||
},
|
||||
update() {
|
||||
if (this.$refs.topology) {
|
||||
this.$refs.topology.update();
|
||||
computed: {
|
||||
...mapState("execution", ["execution", "dataTree"])
|
||||
},
|
||||
created() {
|
||||
if (!this.dataTree && this.execution) {
|
||||
this.$store.dispatch("execution/loadTree", this.execution)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getLabel(node) {
|
||||
return node.data.taskId;
|
||||
},
|
||||
update() {
|
||||
if (this.$refs.topology) {
|
||||
this.$refs.topology.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
v-if="isFile(value)"
|
||||
target="_blank"
|
||||
:href="itemUrl(value)"
|
||||
><download /> {{ $t('download') }}
|
||||
>
|
||||
<download /> {{ $t('download') }}
|
||||
</b-link>
|
||||
<span v-else v-html="value"></span>
|
||||
<span v-else v-html="value" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -26,11 +27,13 @@
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
execution: {
|
||||
type: Object,
|
||||
required: false
|
||||
required: false,
|
||||
default: undefined
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,13 +15,15 @@
|
||||
|
||||
<template v-slot:thead-top v-if="title">
|
||||
<b-tr class="top">
|
||||
<b-th colspan="2">{{ title }}</b-th>
|
||||
<b-th colspan="2">
|
||||
{{ title }}
|
||||
</b-th>
|
||||
</b-tr>
|
||||
</template>
|
||||
|
||||
<template v-slot:empty>
|
||||
<div class="alert alert-info mb-0" role="alert">
|
||||
{{ $t("no data current task")}}
|
||||
{{ $t("no data current task") }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -46,11 +48,13 @@
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: false
|
||||
required: false,
|
||||
default: undefined
|
||||
},
|
||||
execution: {
|
||||
type: Object,
|
||||
required: false
|
||||
required: false,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<flow-edit v-if="flow" :prevent-route-info="true"/>
|
||||
<flow-edit v-if="flow" :prevent-route-info="true" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import FlowEdit from "./FlowEdit";
|
||||
export default {
|
||||
components: { FlowEdit },
|
||||
computed: {
|
||||
...mapGetters("flow", ["flow"])
|
||||
}
|
||||
};
|
||||
import {mapGetters} from "vuex";
|
||||
import FlowEdit from "./FlowEdit";
|
||||
export default {
|
||||
components: {FlowEdit},
|
||||
computed: {
|
||||
...mapGetters("flow", ["flow"])
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -1,64 +1,65 @@
|
||||
<template>
|
||||
<div class="container" v-if="flow">
|
||||
<b-form v-hotkey="keymap" @submit.prevent="onSubmit">
|
||||
<b-alert v-if="flow.triggers" variant="warning" show>{{$t('warning flow with triggers')}}</b-alert>
|
||||
<b-alert v-if="flow.triggers" variant="warning" show>
|
||||
{{ $t('warning flow with triggers') }}
|
||||
</b-alert>
|
||||
|
||||
<b-form-group
|
||||
v-for="input in flow.inputs"
|
||||
:key="input.id"
|
||||
:label="input.name"
|
||||
label-cols-sm="2"
|
||||
label-align-sm="right"
|
||||
label-size="sm"
|
||||
v-for="input in flow.inputs"
|
||||
:key="input.id"
|
||||
:label="input.name"
|
||||
label-cols-sm="2"
|
||||
label-align-sm="right"
|
||||
label-size="sm"
|
||||
>
|
||||
<b-form-input
|
||||
v-if="input.type === 'STRING'"
|
||||
v-model="input.value"
|
||||
type="text"
|
||||
:required="input.required"
|
||||
:placeholder="`${placeholder} ${input.name}`"
|
||||
></b-form-input>
|
||||
v-if="input.type === 'STRING'"
|
||||
v-model="input.value"
|
||||
type="text"
|
||||
:required="input.required"
|
||||
:placeholder="`${placeholder} ${input.name}`"
|
||||
/>
|
||||
<b-form-input
|
||||
v-if="input.type === 'INT'"
|
||||
v-model="input.value"
|
||||
type="number"
|
||||
step="1"
|
||||
:required="input.required"
|
||||
:placeholder="`${placeholder} ${input.name}`"
|
||||
></b-form-input>
|
||||
v-if="input.type === 'INT'"
|
||||
v-model="input.value"
|
||||
type="number"
|
||||
step="1"
|
||||
:required="input.required"
|
||||
:placeholder="`${placeholder} ${input.name}`"
|
||||
/>
|
||||
<b-form-input
|
||||
v-if="input.type === 'FLOAT'"
|
||||
v-model="input.value"
|
||||
type="number"
|
||||
step="0.001"
|
||||
:required="input.required"
|
||||
:placeholder="`${placeholder} ${input.name}`"
|
||||
></b-form-input>
|
||||
v-if="input.type === 'FLOAT'"
|
||||
v-model="input.value"
|
||||
type="number"
|
||||
step="0.001"
|
||||
:required="input.required"
|
||||
:placeholder="`${placeholder} ${input.name}`"
|
||||
/>
|
||||
<date-picker
|
||||
v-if="input.type === 'DATETIME'"
|
||||
v-model="input.value"
|
||||
:required="input.required"
|
||||
type="datetime"
|
||||
class="w-100"
|
||||
:placeholder="$t('select datetime')"
|
||||
></date-picker>
|
||||
v-if="input.type === 'DATETIME'"
|
||||
v-model="input.value"
|
||||
:required="input.required"
|
||||
type="datetime"
|
||||
class="w-100"
|
||||
:placeholder="$t('select datetime')"
|
||||
/>
|
||||
<b-form-file
|
||||
v-if="input.type === 'FILE'"
|
||||
v-model="input.value"
|
||||
:required="input.required"
|
||||
:state="Boolean(input.value)"
|
||||
:placeholder="$t('choose file')"
|
||||
></b-form-file>
|
||||
v-if="input.type === 'FILE'"
|
||||
v-model="input.value"
|
||||
:required="input.required"
|
||||
:state="Boolean(input.value)"
|
||||
:placeholder="$t('choose file')"
|
||||
/>
|
||||
</b-form-group>
|
||||
<b-form-group class="text-right mb-0">
|
||||
<b-button type="submit" variant="primary">
|
||||
{{$t('launch execution')}}
|
||||
<trigger title/>
|
||||
{{ $t('launch execution') }}
|
||||
<trigger title />
|
||||
</b-button>
|
||||
|
||||
</b-form-group>
|
||||
</b-form>
|
||||
<br/>
|
||||
<br>
|
||||
<b-card :header="$t('triggers')" v-if="flow && flow.triggers">
|
||||
<triggers />
|
||||
</b-card>
|
||||
@@ -79,7 +80,7 @@
|
||||
},
|
||||
keymap () {
|
||||
return {
|
||||
'ctrl+enter': this.onSubmit,
|
||||
"ctrl+enter": this.onSubmit,
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -110,13 +111,13 @@
|
||||
formData
|
||||
})
|
||||
.then(response => {
|
||||
this.$store.commit('execution/setExecution', response.data)
|
||||
this.$router.push({name: 'executionEdit', params: response.data})
|
||||
this.$store.commit("execution/setExecution", response.data)
|
||||
this.$router.push({name: "executionEdit", params: response.data})
|
||||
|
||||
return response.data;
|
||||
})
|
||||
.then((execution) => {
|
||||
this.$toast().success(this.$t('triggered done', {name: execution.id}));
|
||||
this.$toast().success(this.$t("triggered done", {name: execution.id}));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,45 +3,45 @@
|
||||
<b-dropdown-item>
|
||||
<router-link :to="`/flows/edit/${actionFlow.namespace}/${actionFlow.id}`">
|
||||
<edit />
|
||||
{{$t('edit flow')}} {{actionFlow.id}}
|
||||
{{ $t('edit flow') }} {{ actionFlow.id }}
|
||||
</router-link>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item>
|
||||
<router-link :to="`/executions/${actionFlow.namespace}/${actionFlow.id}`">
|
||||
<search />
|
||||
{{$tc('display flow {id} executions', null, flow)}}
|
||||
{{ $tc('display flow {id} executions', null, flow) }}
|
||||
</router-link>
|
||||
</b-dropdown-item>
|
||||
<b-dropdown-item>
|
||||
<router-link :to="{name: 'flowTopology', params: actionFlow}">
|
||||
<graph />
|
||||
{{$t('display topology for flow')}} {{actionFlow.id}}
|
||||
{{ $t('display topology for flow') }} {{ actionFlow.id }}
|
||||
</router-link>
|
||||
</b-dropdown-item>
|
||||
</b-dropdown>
|
||||
</template>
|
||||
<script>
|
||||
import Search from "vue-material-design-icons/Magnify";
|
||||
import Edit from "vue-material-design-icons/Pencil";
|
||||
import Graph from "vue-material-design-icons/Graph";
|
||||
import { mapState } from "vuex";
|
||||
export default {
|
||||
components: {
|
||||
Search,
|
||||
Edit,
|
||||
Graph
|
||||
},
|
||||
props: {
|
||||
flowItem: {
|
||||
type: Object,
|
||||
required: false
|
||||
import Search from "vue-material-design-icons/Magnify";
|
||||
import Edit from "vue-material-design-icons/Pencil";
|
||||
import Graph from "vue-material-design-icons/Graph";
|
||||
import {mapState} from "vuex";
|
||||
export default {
|
||||
components: {
|
||||
Search,
|
||||
Edit,
|
||||
Graph
|
||||
},
|
||||
props: {
|
||||
flowItem: {
|
||||
type: Object,
|
||||
default: undefined,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["flow"]),
|
||||
actionFlow() {
|
||||
return this.flow || this.flowItem;
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["flow"]),
|
||||
actionFlow() {
|
||||
return this.flow || this.flowItem;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<editor @onSave="save" v-model="content" lang="yaml"></editor>
|
||||
<editor @onSave="save" v-model="content" lang="yaml" />
|
||||
<bottom-line v-if="canSave || canDelete">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<b-button class="btn-danger" v-if="canDelete" @click="deleteFile">
|
||||
<delete />
|
||||
<span>{{$t('delete')}}</span>
|
||||
<span>{{ $t('delete') }}</span>
|
||||
</b-button>
|
||||
|
||||
<b-button @click="save" v-if="canSave">
|
||||
<content-save />
|
||||
<span>{{$t('save')}}</span>
|
||||
<span>{{ $t('save') }}</span>
|
||||
</b-button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -20,21 +20,21 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import flowTemplateEdit from "../../mixins/flowTemplateEdit";
|
||||
import { mapGetters } from "vuex";
|
||||
import flowTemplateEdit from "../../mixins/flowTemplateEdit";
|
||||
import {mapGetters} from "vuex";
|
||||
|
||||
export default {
|
||||
mixins: [flowTemplateEdit],
|
||||
data() {
|
||||
return {
|
||||
dataType: "flow",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters("flow", ["flow"]),
|
||||
},
|
||||
created() {
|
||||
this.loadFile();
|
||||
},
|
||||
};
|
||||
export default {
|
||||
mixins: [flowTemplateEdit],
|
||||
data() {
|
||||
return {
|
||||
dataType: "flow",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters("flow", ["flow"]),
|
||||
},
|
||||
created() {
|
||||
this.loadFile();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -20,139 +20,139 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Overview from "./Overview";
|
||||
import Schedule from "./Schedule";
|
||||
import DataSource from "./DataSource";
|
||||
import Revisions from "./Revisions";
|
||||
import ExecutionConfiguration from "./ExecutionConfiguration";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import FlowActions from "./FlowActions";
|
||||
import Logs from "../logs/LogsWrapper";
|
||||
import Executions from "../executions/Executions";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import { mapState } from "vuex";
|
||||
import permission from "../../models/permission";
|
||||
import action from "../../models/action";
|
||||
import Overview from "./Overview";
|
||||
import Schedule from "./Schedule";
|
||||
import DataSource from "./DataSource";
|
||||
import Revisions from "./Revisions";
|
||||
import ExecutionConfiguration from "./ExecutionConfiguration";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import FlowActions from "./FlowActions";
|
||||
import Logs from "../logs/LogsWrapper";
|
||||
import Executions from "../executions/Executions";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import {mapState} from "vuex";
|
||||
import permission from "../../models/permission";
|
||||
import action from "../../models/action";
|
||||
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
components: {
|
||||
Overview,
|
||||
Schedule,
|
||||
BottomLine,
|
||||
DataSource,
|
||||
FlowActions,
|
||||
Executions,
|
||||
ExecutionConfiguration,
|
||||
Revisions,
|
||||
Logs
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch("flow/loadFlow", this.$route.params).then(() => {
|
||||
if (this.flow) {
|
||||
this.$store.dispatch("flow/loadTree", this.flow);
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
setTab(tab) {
|
||||
this.$router.push({
|
||||
name: "flowEdit",
|
||||
params: this.$route.params,
|
||||
query: { tab }
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["flow"]),
|
||||
...mapState("auth", ["user"]),
|
||||
routeInfo() {
|
||||
return {
|
||||
title: this.$route.params.id,
|
||||
breadcrumb: [
|
||||
{
|
||||
label: this.$t("flows"),
|
||||
link: {
|
||||
name: "flowsList"
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$route.params.namespace,
|
||||
link: {
|
||||
name: "flowsList",
|
||||
query: {
|
||||
namespace: this.$route.params.namespace
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$route.params.id,
|
||||
link: {
|
||||
name: "flowEdit",
|
||||
params: {
|
||||
namespace: this.$route.params.namespace,
|
||||
id: this.$route.params.id
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
components: {
|
||||
Overview,
|
||||
Schedule,
|
||||
BottomLine,
|
||||
DataSource,
|
||||
FlowActions,
|
||||
Executions,
|
||||
ExecutionConfiguration,
|
||||
Revisions,
|
||||
Logs
|
||||
},
|
||||
tabs() {
|
||||
const title = title => this.$t(title);
|
||||
const tabs = [
|
||||
{
|
||||
tab: "overview",
|
||||
title: title("overview")
|
||||
},
|
||||
];
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.EXECUTION, action.READ, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "executions",
|
||||
title: title("executions")
|
||||
created() {
|
||||
this.$store.dispatch("flow/loadFlow", this.$route.params).then(() => {
|
||||
if (this.flow) {
|
||||
this.$store.dispatch("flow/loadTree", this.flow);
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
setTab(tab) {
|
||||
this.$router.push({
|
||||
name: "flowEdit",
|
||||
params: this.$route.params,
|
||||
query: {tab}
|
||||
});
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["flow"]),
|
||||
...mapState("auth", ["user"]),
|
||||
routeInfo() {
|
||||
return {
|
||||
title: this.$route.params.id,
|
||||
breadcrumb: [
|
||||
{
|
||||
label: this.$t("flows"),
|
||||
link: {
|
||||
name: "flowsList"
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$route.params.namespace,
|
||||
link: {
|
||||
name: "flowsList",
|
||||
query: {
|
||||
namespace: this.$route.params.namespace
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: this.$route.params.id,
|
||||
link: {
|
||||
name: "flowEdit",
|
||||
params: {
|
||||
namespace: this.$route.params.namespace,
|
||||
id: this.$route.params.id
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
tabs() {
|
||||
const title = title => this.$t(title);
|
||||
const tabs = [
|
||||
{
|
||||
tab: "overview",
|
||||
title: title("overview")
|
||||
},
|
||||
];
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.EXECUTION, action.CREATE, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "execution-configuration",
|
||||
title: title("launch execution")
|
||||
});
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.EXECUTION, action.READ, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "executions",
|
||||
title: title("executions")
|
||||
});
|
||||
}
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.EXECUTION, action.CREATE, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "execution-configuration",
|
||||
title: title("launch execution")
|
||||
});
|
||||
}
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.FLOW, action.UPDATE, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "data-source",
|
||||
title: title("source"),
|
||||
class: "p-0"
|
||||
});
|
||||
|
||||
tabs.push({
|
||||
tab: "schedule",
|
||||
title: title("schedule"),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.FLOW, action.READ, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "revisions",
|
||||
title: title("revisions")
|
||||
});
|
||||
}
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.FLOW, action.READ, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "logs",
|
||||
title: title("logs")
|
||||
});
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.FLOW, action.UPDATE, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "data-source",
|
||||
title: title("source"),
|
||||
class: "p-0"
|
||||
});
|
||||
|
||||
tabs.push({
|
||||
tab: "schedule",
|
||||
title: title("schedule"),
|
||||
});
|
||||
}
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.FLOW, action.READ, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "revisions",
|
||||
title: title("revisions")
|
||||
});
|
||||
}
|
||||
|
||||
if (this.user && this.flow && this.user.isAllowed(permission.FLOW, action.READ, this.flow.namespace)) {
|
||||
tabs.push({
|
||||
tab: "logs",
|
||||
title: title("logs")
|
||||
});
|
||||
}
|
||||
|
||||
return tabs;
|
||||
},
|
||||
destroyed () {
|
||||
this.$store.commit("flow/setFlow", undefined)
|
||||
}
|
||||
},
|
||||
destroyed () {
|
||||
this.$store.commit('flow/setFlow', undefined)
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
show-empty
|
||||
>
|
||||
<template #empty>
|
||||
<span class="text-black-50">{{$t('no result')}}</span>
|
||||
<span class="text-black-50">{{ $t('no result') }}</span>
|
||||
</template>
|
||||
|
||||
<template v-slot:cell(actions)="row">
|
||||
@@ -63,20 +63,21 @@
|
||||
<template v-slot:cell(id)="row">
|
||||
<router-link
|
||||
:to="{name: 'flowEdit', params: {namespace: row.item.namespace, id: row.item.id}, query:{tab: 'executions'}}"
|
||||
>{{row.item.id}}</router-link>
|
||||
>
|
||||
{{ row.item.id }}
|
||||
</router-link>
|
||||
<markdown-tooltip :id="row.item.namespace + '-' + row.item.id" :description="row.item.description" />
|
||||
</template>
|
||||
|
||||
<template v-slot:cell(triggers)="row">
|
||||
<trigger-avatar @showTriggerDetails="showTriggerDetails" :flow="row.item"/>
|
||||
<trigger-avatar @showTriggerDetails="showTriggerDetails" :flow="row.item" />
|
||||
</template>
|
||||
|
||||
</b-table>
|
||||
</template>
|
||||
</data-table>
|
||||
</div>
|
||||
|
||||
<trigger-details-modal :trigger="flowTriggerDetails"/>
|
||||
<trigger-details-modal :trigger="flowTriggerDetails" />
|
||||
|
||||
<bottom-line v-if="user && user.hasAnyAction(permission.FLOW, action.CREATE)">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
@@ -84,7 +85,7 @@
|
||||
<router-link :to="{name: 'flowsAdd'}">
|
||||
<b-button variant="primary">
|
||||
<plus />
|
||||
{{$t('create')}}
|
||||
{{ $t('create') }}
|
||||
</b-button>
|
||||
</router-link>
|
||||
</li>
|
||||
@@ -94,156 +95,156 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import permission from "../../models/permission";
|
||||
import action from "../../models/action";
|
||||
import NamespaceSelect from "../namespace/NamespaceSelect";
|
||||
import Plus from "vue-material-design-icons/Plus";
|
||||
import Eye from "vue-material-design-icons/Eye";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import DataTableActions from "../../mixins/dataTableActions";
|
||||
import DataTable from "../layout/DataTable";
|
||||
import SearchField from "../layout/SearchField";
|
||||
import StateChart from "../stats/StateChart";
|
||||
import DurationChart from "../stats/DurationChart";
|
||||
import StateGlobalChart from "../stats/StateGlobalChart";
|
||||
import TriggerDetailsModal from "./TriggerDetailsModal";
|
||||
import TriggerAvatar from "./TriggerAvatar";
|
||||
import MarkdownTooltip from "../layout/MarkdownTooltip"
|
||||
import {mapState} from "vuex";
|
||||
import permission from "../../models/permission";
|
||||
import action from "../../models/action";
|
||||
import NamespaceSelect from "../namespace/NamespaceSelect";
|
||||
import Plus from "vue-material-design-icons/Plus";
|
||||
import Eye from "vue-material-design-icons/Eye";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import DataTableActions from "../../mixins/dataTableActions";
|
||||
import DataTable from "../layout/DataTable";
|
||||
import SearchField from "../layout/SearchField";
|
||||
import StateChart from "../stats/StateChart";
|
||||
import DurationChart from "../stats/DurationChart";
|
||||
import StateGlobalChart from "../stats/StateGlobalChart";
|
||||
import TriggerDetailsModal from "./TriggerDetailsModal";
|
||||
import TriggerAvatar from "./TriggerAvatar";
|
||||
import MarkdownTooltip from "../layout/MarkdownTooltip"
|
||||
|
||||
export default {
|
||||
mixins: [RouteContext, DataTableActions],
|
||||
components: {
|
||||
NamespaceSelect,
|
||||
BottomLine,
|
||||
Plus,
|
||||
Eye,
|
||||
DataTable,
|
||||
SearchField,
|
||||
StateChart,
|
||||
DurationChart,
|
||||
StateGlobalChart,
|
||||
TriggerDetailsModal,
|
||||
TriggerAvatar,
|
||||
MarkdownTooltip
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataType: "flow",
|
||||
permission: permission,
|
||||
action: action,
|
||||
dailyGroupByFlowReady: false,
|
||||
dailyReady: false,
|
||||
flowTriggerDetails: undefined
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["flows", "total"]),
|
||||
...mapState("stat", ["dailyGroupByFlow", "daily"]),
|
||||
...mapState("auth", ["user"]),
|
||||
fields() {
|
||||
const title = title => {
|
||||
return this.$t(title);
|
||||
export default {
|
||||
mixins: [RouteContext, DataTableActions],
|
||||
components: {
|
||||
NamespaceSelect,
|
||||
BottomLine,
|
||||
Plus,
|
||||
Eye,
|
||||
DataTable,
|
||||
SearchField,
|
||||
StateChart,
|
||||
DurationChart,
|
||||
StateGlobalChart,
|
||||
TriggerDetailsModal,
|
||||
TriggerAvatar,
|
||||
MarkdownTooltip
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataType: "flow",
|
||||
permission: permission,
|
||||
action: action,
|
||||
dailyGroupByFlowReady: false,
|
||||
dailyReady: false,
|
||||
flowTriggerDetails: undefined
|
||||
};
|
||||
return [
|
||||
{
|
||||
key: "id",
|
||||
label: title("flow"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "namespace",
|
||||
label: title("namespace"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "state",
|
||||
label: title("execution statistics"),
|
||||
sortable: false,
|
||||
class: "row-graph"
|
||||
},
|
||||
{
|
||||
key: "duration",
|
||||
label: title("duration"),
|
||||
sortable: false,
|
||||
class: "row-graph"
|
||||
},
|
||||
{
|
||||
key: "triggers",
|
||||
label: title("triggers"),
|
||||
class: "shrink"
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
label: "",
|
||||
class: "row-action"
|
||||
}
|
||||
];
|
||||
},
|
||||
endDate() {
|
||||
return new Date();
|
||||
},
|
||||
startDate() {
|
||||
return this.$moment(this.endDate)
|
||||
.add(-30, "days")
|
||||
.toDate();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showTriggerDetails(trigger) {
|
||||
this.flowTriggerDetails = trigger
|
||||
this.$bvModal.show('modal-triggers-details')
|
||||
},
|
||||
chartData(row) {
|
||||
if (this.dailyGroupByFlow && this.dailyGroupByFlow[row.item.namespace] && this.dailyGroupByFlow[row.item.namespace][row.item.id]) {
|
||||
return this.dailyGroupByFlow[row.item.namespace][row.item.id];
|
||||
} else {
|
||||
return [];
|
||||
computed: {
|
||||
...mapState("flow", ["flows", "total"]),
|
||||
...mapState("stat", ["dailyGroupByFlow", "daily"]),
|
||||
...mapState("auth", ["user"]),
|
||||
fields() {
|
||||
const title = title => {
|
||||
return this.$t(title);
|
||||
};
|
||||
return [
|
||||
{
|
||||
key: "id",
|
||||
label: title("flow"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "namespace",
|
||||
label: title("namespace"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "state",
|
||||
label: title("execution statistics"),
|
||||
sortable: false,
|
||||
class: "row-graph"
|
||||
},
|
||||
{
|
||||
key: "duration",
|
||||
label: title("duration"),
|
||||
sortable: false,
|
||||
class: "row-graph"
|
||||
},
|
||||
{
|
||||
key: "triggers",
|
||||
label: title("triggers"),
|
||||
class: "shrink"
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
label: "",
|
||||
class: "row-action"
|
||||
}
|
||||
];
|
||||
},
|
||||
endDate() {
|
||||
return new Date();
|
||||
},
|
||||
startDate() {
|
||||
return this.$moment(this.endDate)
|
||||
.add(-30, "days")
|
||||
.toDate();
|
||||
}
|
||||
},
|
||||
loadData(callback) {
|
||||
this.dailyReady = false;
|
||||
this.$store
|
||||
.dispatch("stat/daily", {
|
||||
q: this.query.replace("id:" , "flowId:"),
|
||||
startDate: this.$moment(this.startDate).format('YYYY-MM-DD'),
|
||||
endDate: this.$moment(this.endDate).format('YYYY-MM-DD')
|
||||
})
|
||||
.then(() => {
|
||||
this.dailyReady = true;
|
||||
});
|
||||
methods: {
|
||||
showTriggerDetails(trigger) {
|
||||
this.flowTriggerDetails = trigger
|
||||
this.$bvModal.show("modal-triggers-details")
|
||||
},
|
||||
chartData(row) {
|
||||
if (this.dailyGroupByFlow && this.dailyGroupByFlow[row.item.namespace] && this.dailyGroupByFlow[row.item.namespace][row.item.id]) {
|
||||
return this.dailyGroupByFlow[row.item.namespace][row.item.id];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
loadData(callback) {
|
||||
this.dailyReady = false;
|
||||
this.$store
|
||||
.dispatch("stat/daily", {
|
||||
q: this.query.replace("id:" , "flowId:"),
|
||||
startDate: this.$moment(this.startDate).format("YYYY-MM-DD"),
|
||||
endDate: this.$moment(this.endDate).format("YYYY-MM-DD")
|
||||
})
|
||||
.then(() => {
|
||||
this.dailyReady = true;
|
||||
});
|
||||
|
||||
this.$store
|
||||
.dispatch("flow/findFlows", {
|
||||
q: this.query,
|
||||
size: parseInt(this.$route.query.size || 25),
|
||||
page: parseInt(this.$route.query.page || 1),
|
||||
sort: this.$route.query.sort
|
||||
})
|
||||
.then(flows => {
|
||||
this.dailyGroupByFlowReady = false;
|
||||
callback();
|
||||
this.$store
|
||||
.dispatch("flow/findFlows", {
|
||||
q: this.query,
|
||||
size: parseInt(this.$route.query.size || 25),
|
||||
page: parseInt(this.$route.query.page || 1),
|
||||
sort: this.$route.query.sort
|
||||
})
|
||||
.then(flows => {
|
||||
this.dailyGroupByFlowReady = false;
|
||||
callback();
|
||||
|
||||
if (flows.results && flows.results.length > 0) {
|
||||
let query = "((" + flows.results
|
||||
.map(flow => "flowId:" + flow.id + " AND namespace:" + flow.namespace)
|
||||
.join(") OR (") + "))"
|
||||
if (flows.results && flows.results.length > 0) {
|
||||
let query = "((" + flows.results
|
||||
.map(flow => "flowId:" + flow.id + " AND namespace:" + flow.namespace)
|
||||
.join(") OR (") + "))"
|
||||
|
||||
this.$store
|
||||
.dispatch("stat/dailyGroupByFlow", {
|
||||
q: query,
|
||||
startDate: this.$moment(this.startDate).format('YYYY-MM-DD'),
|
||||
endDate: this.$moment(this.endDate).format('YYYY-MM-DD')
|
||||
})
|
||||
.then(() => {
|
||||
this.dailyGroupByFlowReady = true
|
||||
})
|
||||
}
|
||||
})
|
||||
this.$store
|
||||
.dispatch("stat/dailyGroupByFlow", {
|
||||
q: query,
|
||||
startDate: this.$moment(this.startDate).format("YYYY-MM-DD"),
|
||||
endDate: this.$moment(this.endDate).format("YYYY-MM-DD")
|
||||
})
|
||||
.then(() => {
|
||||
this.dailyGroupByFlowReady = true
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../styles/_variable.scss";
|
||||
|
||||
@@ -2,36 +2,36 @@
|
||||
<div>
|
||||
<b-row class="topology-wrapper">
|
||||
<b-col>
|
||||
<topology-tree :isFlow="true" v-if="flow && dataTree" :dataTree="dataTree" :label="getLabel"/>
|
||||
<topology-tree :is-flow="true" v-if="flow && dataTree" :data-tree="dataTree" :label="getLabel" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col>
|
||||
<task-details/>
|
||||
<task-details />
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import TopologyTree from "../graph/TopologyTree";
|
||||
import TaskDetails from "./TaskDetails";
|
||||
import {mapState} from "vuex";
|
||||
import TopologyTree from "../graph/TopologyTree";
|
||||
import TaskDetails from "./TaskDetails";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TopologyTree,
|
||||
TaskDetails
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["flow", "dataTree"]),
|
||||
},
|
||||
methods: {
|
||||
getLabel (node) {
|
||||
const id = node.data.id;
|
||||
return `${id.substr(0, 25)}${id.length > 25 ? "..." : ""}`;
|
||||
export default {
|
||||
components: {
|
||||
TopologyTree,
|
||||
TaskDetails
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["flow", "dataTree"]),
|
||||
},
|
||||
methods: {
|
||||
getLabel (node) {
|
||||
const id = node.data.id;
|
||||
return `${id.substr(0, 25)}${id.length > 25 ? "..." : ""}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.topology-wrapper {
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
<div v-if="revisions && revisions.length > 1">
|
||||
<b-row>
|
||||
<b-col md="12">
|
||||
<b-form-select v-model="displayType" :options="displayTypes"></b-form-select>
|
||||
<hr />
|
||||
<b-form-select v-model="displayType" :options="displayTypes" />
|
||||
<hr>
|
||||
</b-col>
|
||||
|
||||
<b-col md="6">
|
||||
<b-form-select v-model="revisionLeft" :options="options"></b-form-select>
|
||||
<b-form-select v-model="revisionLeft" :options="options" />
|
||||
</b-col>
|
||||
<b-col md="6">
|
||||
<b-form-select v-model="revisionRight" :options="options"></b-form-select>
|
||||
<b-form-select v-model="revisionRight" :options="options" />
|
||||
</b-col>
|
||||
<b-col md="12">
|
||||
<br />
|
||||
<br>
|
||||
<code-diff
|
||||
:outputFormat="displayType"
|
||||
:output-format="displayType"
|
||||
:old-string="revisionLeftText"
|
||||
:new-string="revisionRightText"
|
||||
:context="10"
|
||||
@@ -24,98 +24,100 @@
|
||||
</b-row>
|
||||
</div>
|
||||
<div v-else>
|
||||
<b-alert class="mb-0" show>{{$t('no revisions found')}}</b-alert>
|
||||
<b-alert class="mb-0" show>
|
||||
{{ $t('no revisions found') }}
|
||||
</b-alert>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import YamlUtils from "../../utils/yamlUtils";
|
||||
import CodeDiff from "vue-code-diff";
|
||||
import {mapState} from "vuex";
|
||||
import YamlUtils from "../../utils/yamlUtils";
|
||||
import CodeDiff from "vue-code-diff";
|
||||
|
||||
export default {
|
||||
components: { CodeDiff },
|
||||
created() {
|
||||
this.$store
|
||||
.dispatch("flow/loadRevisions", this.$route.params)
|
||||
.then(() => {
|
||||
const revisionLength = this.revisions.length;
|
||||
if (revisionLength > 0) {
|
||||
this.revisionRight = revisionLength - 1;
|
||||
}
|
||||
if (revisionLength > 1) {
|
||||
this.revisionLeft = revisionLength - 2;
|
||||
}
|
||||
if (this.$route.query.revisionRight) {
|
||||
this.revisionRight = this.revisionIndex(
|
||||
this.$route.query.revisionRight
|
||||
);
|
||||
if (
|
||||
!this.$route.query.revisionLeft &&
|
||||
this.revisionRight > 0
|
||||
) {
|
||||
this.revisionLeft = this.revisions.length - 1;
|
||||
export default {
|
||||
components: {CodeDiff},
|
||||
created() {
|
||||
this.$store
|
||||
.dispatch("flow/loadRevisions", this.$route.params)
|
||||
.then(() => {
|
||||
const revisionLength = this.revisions.length;
|
||||
if (revisionLength > 0) {
|
||||
this.revisionRight = revisionLength - 1;
|
||||
}
|
||||
if (revisionLength > 1) {
|
||||
this.revisionLeft = revisionLength - 2;
|
||||
}
|
||||
if (this.$route.query.revisionRight) {
|
||||
this.revisionRight = this.revisionIndex(
|
||||
this.$route.query.revisionRight
|
||||
);
|
||||
if (
|
||||
!this.$route.query.revisionLeft &&
|
||||
this.revisionRight > 0
|
||||
) {
|
||||
this.revisionLeft = this.revisions.length - 1;
|
||||
}
|
||||
}
|
||||
if (this.$route.query.revisionLeft) {
|
||||
this.revisionLeft = this.revisionIndex(
|
||||
this.$route.query.revisionLeft
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
revisionIndex(revision) {
|
||||
const rev = parseInt(revision);
|
||||
for (let i = 0; i < this.revisions.length; i++) {
|
||||
if (rev === this.revisions[i].revision) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if (this.$route.query.revisionLeft) {
|
||||
this.revisionLeft = this.revisionIndex(
|
||||
this.$route.query.revisionLeft
|
||||
);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["revisions"]),
|
||||
options() {
|
||||
return (this.revisions || []).map((revision, x) => {
|
||||
return {
|
||||
value: x,
|
||||
text: revision.revision,
|
||||
};
|
||||
});
|
||||
},
|
||||
revisionLeftText() {
|
||||
if (this.revisionLeft === undefined) {
|
||||
return "";
|
||||
}
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
revisionIndex(revision) {
|
||||
const rev = parseInt(revision);
|
||||
for (let i = 0; i < this.revisions.length; i++) {
|
||||
if (rev === this.revisions[i].revision) {
|
||||
return i;
|
||||
return YamlUtils.stringify(this.revisions[this.revisionLeft]);
|
||||
},
|
||||
revisionRightText() {
|
||||
if (this.revisionRight === undefined) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return YamlUtils.stringify(this.revisions[this.revisionRight]);
|
||||
},
|
||||
diff() {
|
||||
const linesLeft = this.revisionLeftText.split("\n");
|
||||
const linesRight = this.revisionRightText.split("\n");
|
||||
const minLength = Math.min(linesLeft.length, linesRight.length);
|
||||
const diff = [];
|
||||
for (let i = 0; i < minLength; i++) {
|
||||
diff.push(linesLeft[i] === linesRight[i] ? "" : "≠");
|
||||
}
|
||||
return diff.join("\n");
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["revisions"]),
|
||||
options() {
|
||||
return (this.revisions || []).map((revision, x) => {
|
||||
return {
|
||||
value: x,
|
||||
text: revision.revision,
|
||||
};
|
||||
});
|
||||
data() {
|
||||
return {
|
||||
revisionLeft: 0,
|
||||
revisionRight: 0,
|
||||
displayType: "side-by-side",
|
||||
displayTypes: [
|
||||
{value: "side-by-side", text: "side-by-side"},
|
||||
{value: "line-by-line", text: "line-by-line"},
|
||||
],
|
||||
};
|
||||
},
|
||||
revisionLeftText() {
|
||||
if (this.revisionLeft === undefined) {
|
||||
return "";
|
||||
}
|
||||
return YamlUtils.stringify(this.revisions[this.revisionLeft]);
|
||||
},
|
||||
revisionRightText() {
|
||||
if (this.revisionRight === undefined) {
|
||||
return "";
|
||||
}
|
||||
return YamlUtils.stringify(this.revisions[this.revisionRight]);
|
||||
},
|
||||
diff() {
|
||||
const linesLeft = this.revisionLeftText.split("\n");
|
||||
const linesRight = this.revisionRightText.split("\n");
|
||||
const minLength = Math.min(linesLeft.length, linesRight.length);
|
||||
const diff = [];
|
||||
for (let i = 0; i < minLength; i++) {
|
||||
diff.push(linesLeft[i] === linesRight[i] ? "" : "≠");
|
||||
}
|
||||
return diff.join("\n");
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
revisionLeft: 0,
|
||||
revisionRight: 0,
|
||||
displayType: "side-by-side",
|
||||
displayTypes: [
|
||||
{ value: "side-by-side", text: "side-by-side" },
|
||||
{ value: "line-by-line", text: "line-by-line" },
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -6,21 +6,29 @@
|
||||
@set="set"
|
||||
:schedule="schedule"
|
||||
:index="x"
|
||||
v-for="(schedule, x) in (flow.triggers || []).filter(r => r.type === 'org.kestra.core.models.triggers.types.Schedule')"
|
||||
v-for="(schedule, x) in (flow.triggers || []).filter(
|
||||
(r) =>
|
||||
r.type ===
|
||||
'org.kestra.core.models.triggers.types.Schedule'
|
||||
)"
|
||||
:key="x"
|
||||
/>
|
||||
</b-list-group>
|
||||
<bottom-line v-if="canSave">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<b-button variant="primary" @click="addSchedule" v-if="canSave">
|
||||
<b-button
|
||||
variant="primary"
|
||||
@click="addSchedule"
|
||||
v-if="canSave"
|
||||
>
|
||||
<plus />
|
||||
{{ $t('add schedule') }}
|
||||
{{ $t("add schedule") }}
|
||||
</b-button>
|
||||
|
||||
<b-button @click="save" v-if="canSave">
|
||||
<content-save />
|
||||
<span>{{$t('save')}}</span>
|
||||
<span>{{ $t("save") }}</span>
|
||||
</b-button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -28,46 +36,49 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import ContentSave from "vue-material-design-icons/ContentSave";
|
||||
import Plus from "vue-material-design-icons/Plus";
|
||||
import ScheduleItem from "./ScheduleItem";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import { canSaveFlowTemplate, saveFlowTemplate } from "../../utils/flowTemplate";
|
||||
import {mapState} from "vuex";
|
||||
import ContentSave from "vue-material-design-icons/ContentSave";
|
||||
import Plus from "vue-material-design-icons/Plus";
|
||||
import ScheduleItem from "./ScheduleItem";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import {
|
||||
canSaveFlowTemplate,
|
||||
saveFlowTemplate,
|
||||
} from "../../utils/flowTemplate";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Plus,
|
||||
ContentSave,
|
||||
ScheduleItem,
|
||||
BottomLine
|
||||
},
|
||||
computed: {
|
||||
...mapState("flow", ["flow"]),
|
||||
...mapState("auth", ["user"]),
|
||||
canSave() {
|
||||
return canSaveFlowTemplate(true, this.user, this.flow, "flow");
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
saveFlowTemplate(this, this.flow, "flow");
|
||||
export default {
|
||||
components: {
|
||||
Plus,
|
||||
ContentSave,
|
||||
ScheduleItem,
|
||||
BottomLine,
|
||||
},
|
||||
set(index, schedule) {
|
||||
this.$store.commit("flow/setTrigger", {index, trigger: schedule});
|
||||
computed: {
|
||||
...mapState("flow", ["flow"]),
|
||||
...mapState("auth", ["user"]),
|
||||
canSave() {
|
||||
return canSaveFlowTemplate(true, this.user, this.flow, "flow");
|
||||
},
|
||||
},
|
||||
remove(index) {
|
||||
this.$store.commit("flow/removeTrigger", index);
|
||||
methods: {
|
||||
save() {
|
||||
saveFlowTemplate(this, this.flow, "flow");
|
||||
},
|
||||
set(index, schedule) {
|
||||
this.$store.commit("flow/setTrigger", {index, trigger: schedule});
|
||||
},
|
||||
remove(index) {
|
||||
this.$store.commit("flow/removeTrigger", index);
|
||||
},
|
||||
addSchedule() {
|
||||
this.$store.commit("flow/addTrigger", {
|
||||
id: "schedule",
|
||||
cron: "0 4 * * 1,4",
|
||||
type: "org.kestra.core.models.triggers.types.Schedule",
|
||||
});
|
||||
},
|
||||
},
|
||||
addSchedule() {
|
||||
this.$store.commit("flow/addTrigger", {
|
||||
id: "schedule",
|
||||
cron: "0 4 * * 1,4",
|
||||
type: "org.kestra.core.models.triggers.types.Schedule",
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,29 @@
|
||||
<template>
|
||||
<b-list-group-item>
|
||||
<b-form-group label-cols-sm="3" label-cols-lg="2" :label="$t('id')" :label-for="'input-id-' + index">
|
||||
<b-form-input required :id="'input-id-' + index" v-model="schedule.id"></b-form-input>
|
||||
<b-form-input required :id="'input-id-' + index" v-model="schedule.id" />
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group label-cols-sm="3" label-cols-lg="2"
|
||||
:label-for="'input-cron-' + index"
|
||||
:state="isValid">
|
||||
<b-form-group
|
||||
label-cols-sm="3"
|
||||
label-cols-lg="2"
|
||||
:label-for="'input-cron-' + index"
|
||||
:state="isValid"
|
||||
>
|
||||
<template v-slot:label>
|
||||
{{ $t('schedules.cron.expression')}}
|
||||
{{ $t('schedules.cron.expression') }}
|
||||
<b-link class="text-body" :id="'tooltip-' + index">
|
||||
<help />
|
||||
</b-link>
|
||||
|
||||
<b-tooltip :target="'tooltip-' + index" placement="bottom">
|
||||
<div v-if="isValid">
|
||||
<p class="font-weight-bold">3 Next occurences</p>
|
||||
<p class="font-weight-bold">
|
||||
3 Next occurences
|
||||
</p>
|
||||
|
||||
<span v-if="occurences.length">
|
||||
<span v-for="(occurence, x) in occurences" :key="x">{{occurence | date('LLL:ss')}}<br /></span>
|
||||
<span v-for="(occurence, x) in occurences" :key="x">{{ occurence | date('LLL:ss') }}<br></span>
|
||||
</span>
|
||||
</div>
|
||||
<span v-else>
|
||||
@@ -27,7 +32,7 @@
|
||||
</b-tooltip>
|
||||
</template>
|
||||
|
||||
<b-form-input required :id="'input-cron-' + index" v-model="schedule.cron"></b-form-input>
|
||||
<b-form-input required :id="'input-cron-' + index" v-model="schedule.cron" />
|
||||
|
||||
<b-form-invalid-feedback>
|
||||
Enter at least 3 letters
|
||||
@@ -37,95 +42,98 @@
|
||||
</b-form-group>
|
||||
|
||||
|
||||
<b-form-group label-cols-sm="3" label-cols-lg="2" :label="$t('schedules.cron.backfilll')"
|
||||
:label-for="'input-' + index">
|
||||
<b-form-group
|
||||
label-cols-sm="3"
|
||||
label-cols-lg="2"
|
||||
:label="$t('schedules.cron.backfilll')"
|
||||
:label-for="'input-' + index"
|
||||
>
|
||||
<date-picker
|
||||
v-model="backfillStart"
|
||||
:required="false"
|
||||
type="datetime"
|
||||
:id="'input-' + index"
|
||||
></date-picker>
|
||||
/>
|
||||
</b-form-group>
|
||||
|
||||
<b-form-group class="mb-0 text-right">
|
||||
<b-btn variant="danger" @click="remove">
|
||||
<delete/>
|
||||
<delete />
|
||||
Delete
|
||||
</b-btn>
|
||||
|
||||
</b-form-group>
|
||||
</b-list-group-item>
|
||||
</template>
|
||||
<script>
|
||||
const cronstrue = require("cronstrue/i18n");
|
||||
const cronParser = require("cron-parser");
|
||||
import Delete from "vue-material-design-icons/Delete";
|
||||
import Help from "vue-material-design-icons/HelpBox";
|
||||
import DatePicker from "vue2-datepicker";
|
||||
const cronstrue = require("cronstrue/i18n");
|
||||
const cronParser = require("cron-parser");
|
||||
import Delete from "vue-material-design-icons/Delete";
|
||||
import Help from "vue-material-design-icons/HelpBox";
|
||||
import DatePicker from "vue2-datepicker";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Delete,
|
||||
Help,
|
||||
DatePicker
|
||||
},
|
||||
props: {
|
||||
schedule: {
|
||||
type: Object,
|
||||
required: true
|
||||
export default {
|
||||
components: {
|
||||
Delete,
|
||||
Help,
|
||||
DatePicker
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
backfillStart: {
|
||||
get: function () {
|
||||
return this.schedule.backfill && this.schedule.backfill.start !== undefined ?
|
||||
this.$moment(this.schedule.backfill.start).toDate() :
|
||||
undefined;
|
||||
props: {
|
||||
schedule: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
set: function (val) {
|
||||
let current = this.schedule;
|
||||
index: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
if (val) {
|
||||
current.backfill = {"start": this.$moment(val).format()};
|
||||
} else {
|
||||
delete current.backfill;
|
||||
computed: {
|
||||
backfillStart: {
|
||||
get: function () {
|
||||
return this.schedule.backfill && this.schedule.backfill.start !== undefined ?
|
||||
this.$moment(this.schedule.backfill.start).toDate() :
|
||||
undefined;
|
||||
},
|
||||
set: function (val) {
|
||||
let current = this.schedule;
|
||||
|
||||
if (val) {
|
||||
current.backfill = {"start": this.$moment(val).format()};
|
||||
} else {
|
||||
delete current.backfill;
|
||||
}
|
||||
|
||||
this.$emit("set", this.index, current);
|
||||
}
|
||||
},
|
||||
occurences() {
|
||||
const occurences = [];
|
||||
if (!this.isValid) {
|
||||
return occurences;
|
||||
}
|
||||
const interval = cronParser.parseExpression(this.schedule.cron);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
occurences.push(interval.next().toDate());
|
||||
}
|
||||
|
||||
this.$emit("set", this.index, current);
|
||||
}
|
||||
},
|
||||
occurences() {
|
||||
const occurences = [];
|
||||
if (!this.isValid) {
|
||||
return occurences;
|
||||
}
|
||||
const interval = cronParser.parseExpression(this.schedule.cron);
|
||||
for (let i = 0; i < 3; i++) {
|
||||
occurences.push(interval.next().toDate());
|
||||
}
|
||||
return occurences;
|
||||
},
|
||||
cronHumanReadable() {
|
||||
const locale = localStorage.getItem("lang") || "en";
|
||||
try {
|
||||
return cronstrue.toString(this.schedule.cron, { locale });
|
||||
} catch {
|
||||
return this.$t("schedules.cron.invalid");
|
||||
},
|
||||
cronHumanReadable() {
|
||||
const locale = localStorage.getItem("lang") || "en";
|
||||
try {
|
||||
return cronstrue.toString(this.schedule.cron, {locale});
|
||||
} catch {
|
||||
return this.$t("schedules.cron.invalid");
|
||||
}
|
||||
},
|
||||
isValid() {
|
||||
return require("cron-validator").isValidCron(this.schedule.cron);
|
||||
}
|
||||
},
|
||||
isValid() {
|
||||
return require("cron-validator").isValidCron(this.schedule.cron);
|
||||
methods: {
|
||||
remove() {
|
||||
this.$emit("remove", this.index);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
remove() {
|
||||
this.$emit("remove", this.index);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -10,26 +10,26 @@
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import YamlUtils from "../../utils/yamlUtils";
|
||||
import { mapState } from "vuex";
|
||||
export default {
|
||||
computed: {
|
||||
...mapState("graph", ["node"]),
|
||||
task() {
|
||||
return this.node && this.node.task;
|
||||
},
|
||||
items() {
|
||||
const items = [];
|
||||
for (const property in this.task) {
|
||||
const v = this.task[property];
|
||||
const value =
|
||||
typeof v === "object"
|
||||
? `<pre>${YamlUtils.stringify(v)}</pre>`
|
||||
: v;
|
||||
items.push({ property, value });
|
||||
import YamlUtils from "../../utils/yamlUtils";
|
||||
import {mapState} from "vuex";
|
||||
export default {
|
||||
computed: {
|
||||
...mapState("graph", ["node"]),
|
||||
task() {
|
||||
return this.node && this.node.task;
|
||||
},
|
||||
items() {
|
||||
const items = [];
|
||||
for (const property in this.task) {
|
||||
const v = this.task[property];
|
||||
const value =
|
||||
typeof v === "object"
|
||||
? `<pre>${YamlUtils.stringify(v)}</pre>`
|
||||
: v;
|
||||
items.push({property, value});
|
||||
}
|
||||
return items;
|
||||
}
|
||||
return items;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -7,51 +7,50 @@
|
||||
:text="name(trigger)"
|
||||
button
|
||||
@click="showTriggerDetails(trigger)"
|
||||
>
|
||||
</b-avatar>
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
props: {
|
||||
flow: {
|
||||
type: Object,
|
||||
default: () => undefined,
|
||||
export default {
|
||||
props: {
|
||||
flow: {
|
||||
type: Object,
|
||||
default: () => undefined,
|
||||
},
|
||||
execution: {
|
||||
type: Object,
|
||||
default: () => undefined,
|
||||
},
|
||||
},
|
||||
execution: {
|
||||
type: Object,
|
||||
default: () => undefined,
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
methods: {
|
||||
showTriggerDetails(trigger) {
|
||||
this.$emit("showTriggerDetails", trigger);
|
||||
},
|
||||
uid(trigger) {
|
||||
return (this.flow ? this.flow.namespace + "-" + this.flow.id : this.execution.namespace + "-" + this.execution.flowId) + '-' + trigger.id
|
||||
},
|
||||
name(trigger) {
|
||||
let split = trigger.id.split(".");
|
||||
components: {},
|
||||
methods: {
|
||||
showTriggerDetails(trigger) {
|
||||
this.$emit("showTriggerDetails", trigger);
|
||||
},
|
||||
uid(trigger) {
|
||||
return (this.flow ? this.flow.namespace + "-" + this.flow.id : this.execution.namespace + "-" + this.execution.flowId) + "-" + trigger.id
|
||||
},
|
||||
name(trigger) {
|
||||
let split = trigger.id.split(".");
|
||||
|
||||
return split[split.length - 1].substr(0, 1).toUpperCase();
|
||||
return split[split.length - 1].substr(0, 1).toUpperCase();
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
triggers() {
|
||||
if (this.flow && this.flow.triggers) {
|
||||
return this.flow.triggers
|
||||
} else if (this.execution && this.execution.trigger) {
|
||||
return [this.execution.trigger]
|
||||
} else {
|
||||
return []
|
||||
computed: {
|
||||
triggers() {
|
||||
if (this.flow && this.flow.triggers) {
|
||||
return this.flow.triggers
|
||||
} else if (this.execution && this.execution.trigger) {
|
||||
return [this.execution.trigger]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -11,25 +11,25 @@
|
||||
</b-modal>
|
||||
</template>
|
||||
<script>
|
||||
import Vars from "../executions/Vars";
|
||||
import Markdown from "../../utils/markdown";
|
||||
import Vars from "../executions/Vars";
|
||||
import Markdown from "../../utils/markdown";
|
||||
|
||||
export default {
|
||||
components: { Vars },
|
||||
props: {
|
||||
trigger: {
|
||||
type: Object,
|
||||
default: () => undefined
|
||||
export default {
|
||||
components: {Vars},
|
||||
props: {
|
||||
trigger: {
|
||||
type: Object,
|
||||
default: () => undefined
|
||||
},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
triggerData() {
|
||||
if (this.trigger.description) {
|
||||
return {...this.trigger, description: Markdown.render(this.trigger.description)}
|
||||
}
|
||||
computed: {
|
||||
triggerData() {
|
||||
if (this.trigger.description) {
|
||||
return {...this.trigger, description: Markdown.render(this.trigger.description)}
|
||||
}
|
||||
|
||||
return this.trigger
|
||||
return this.trigger
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -21,19 +21,19 @@
|
||||
role="tabpanel"
|
||||
>
|
||||
<b-card-body>
|
||||
<vars :data="trigger"/>
|
||||
<vars :data="trigger" />
|
||||
</b-card-body>
|
||||
</b-collapse>
|
||||
</b-card>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import Vars from '../executions/Vars'
|
||||
export default {
|
||||
components: { Vars },
|
||||
computed: {
|
||||
...mapState("flow", ["flow"]),
|
||||
}
|
||||
};
|
||||
import {mapState} from "vuex";
|
||||
import Vars from "../executions/Vars"
|
||||
export default {
|
||||
components: {Vars},
|
||||
computed: {
|
||||
...mapState("flow", ["flow"]),
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
/>
|
||||
</b-breadcrumb>
|
||||
<div>
|
||||
<b-tooltip placement="left" target="graph-orientation">{{$t('graph orientation')}}</b-tooltip>
|
||||
<b-tooltip placement="left" target="graph-orientation">
|
||||
{{ $t('graph orientation') }}
|
||||
</b-tooltip>
|
||||
<b-btn size="sm" @click="toggleOrientation" id="graph-orientation">
|
||||
<arrow-collapse-down v-if="orientation" />
|
||||
<arrow-collapse-right v-else />
|
||||
@@ -24,7 +26,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :class="{hide: !ready}" class="wrapper" ref="wrapper"></div>
|
||||
<div :class="{hide: !ready}" class="wrapper" ref="wrapper" />
|
||||
<div class="hidden">
|
||||
<tree-node
|
||||
@onFilterGroup="onFilterGroup"
|
||||
@@ -32,13 +34,13 @@
|
||||
v-for="node in filteredDataTree"
|
||||
:key="slug(node)"
|
||||
:n="node"
|
||||
:isFlow="isFlow"
|
||||
:is-flow="isFlow"
|
||||
/>
|
||||
<tree-node
|
||||
:ref="`node-${slug(virtualRootNode)}`"
|
||||
v-if="virtualRootNode"
|
||||
:n="virtualRootNode"
|
||||
:isFlow="isFlow"
|
||||
:is-flow="isFlow"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -48,290 +50,290 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const dagreD3 = require("dagre-d3");
|
||||
import TreeNode from "./TreeNode";
|
||||
import * as d3 from "d3";
|
||||
import ArrowCollapseRight from "vue-material-design-icons/ArrowCollapseRight";
|
||||
import ArrowCollapseDown from "vue-material-design-icons/ArrowCollapseDown";
|
||||
import VectorCircle from "vue-material-design-icons/Circle";
|
||||
const parentHash = node => {
|
||||
if (node.parent) {
|
||||
const parent = node.parent[0];
|
||||
return nodeHash(parent)
|
||||
} else {
|
||||
return undefined;
|
||||
const dagreD3 = require("dagre-d3");
|
||||
import TreeNode from "./TreeNode";
|
||||
import * as d3 from "d3";
|
||||
import ArrowCollapseRight from "vue-material-design-icons/ArrowCollapseRight";
|
||||
import ArrowCollapseDown from "vue-material-design-icons/ArrowCollapseDown";
|
||||
import VectorCircle from "vue-material-design-icons/Circle";
|
||||
const parentHash = node => {
|
||||
if (node.parent) {
|
||||
const parent = node.parent[0];
|
||||
return nodeHash(parent)
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
const nodeHash = node => {
|
||||
return (node.id + (node.value ? "-" + node.value : "")).hashCode();
|
||||
}
|
||||
};
|
||||
const nodeHash = node => {
|
||||
return (node.id + (node.value ? "-" + node.value : "")).hashCode();
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
TreeNode,
|
||||
ArrowCollapseDown,
|
||||
ArrowCollapseRight,
|
||||
VectorCircle
|
||||
},
|
||||
props: {
|
||||
dataTree: {
|
||||
type: Array,
|
||||
required: true
|
||||
export default {
|
||||
components: {
|
||||
TreeNode,
|
||||
ArrowCollapseDown,
|
||||
ArrowCollapseRight,
|
||||
VectorCircle
|
||||
},
|
||||
label: {
|
||||
type: Function,
|
||||
required: true
|
||||
props: {
|
||||
dataTree: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
label: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
isFlow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
isFlow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ready: false,
|
||||
filterGroup: undefined,
|
||||
orientation: true,
|
||||
zoom: undefined,
|
||||
filteredDataTree: undefined,
|
||||
virtualRootNode: undefined
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
dataTree() {
|
||||
this.generateGraph();
|
||||
data() {
|
||||
return {
|
||||
ready: false,
|
||||
filterGroup: undefined,
|
||||
orientation: true,
|
||||
zoom: undefined,
|
||||
filteredDataTree: undefined,
|
||||
virtualRootNode: undefined
|
||||
};
|
||||
},
|
||||
$route() {
|
||||
if (this.$route.query.filter != this.filterGroup) {
|
||||
this.filterGroup = this.$route.query.filter;
|
||||
watch: {
|
||||
dataTree() {
|
||||
this.generateGraph();
|
||||
},
|
||||
$route() {
|
||||
if (this.$route.query.filter != this.filterGroup) {
|
||||
this.filterGroup = this.$route.query.filter;
|
||||
this.generateGraph();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.orientation = localStorage.getItem("topology-orientation") === "1";
|
||||
if (this.$route.query.filter) {
|
||||
this.filterGroup = this.$route.query.filter;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.generateGraph();
|
||||
},
|
||||
methods: {
|
||||
toggleOrientation() {
|
||||
this.orientation = !this.orientation;
|
||||
localStorage.setItem(
|
||||
"topology-orientation",
|
||||
this.orientation ? 1 : 0
|
||||
);
|
||||
},
|
||||
created() {
|
||||
this.orientation = localStorage.getItem("topology-orientation") === "1";
|
||||
if (this.$route.query.filter) {
|
||||
this.filterGroup = this.$route.query.filter;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.generateGraph();
|
||||
},
|
||||
getVirtualRootNode() {
|
||||
return this.filterGroup
|
||||
? {
|
||||
task: {
|
||||
id: this.filterGroup
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
},
|
||||
generateGraph() {
|
||||
this.filteredDataTree = this.getFilteredDataTree();
|
||||
this.virtualRootNode = this.getVirtualRootNode();
|
||||
// Create the input graph
|
||||
const arrowColor = "#ccc";
|
||||
if (this.zoom) {
|
||||
this.zoom.on("zoom", null);
|
||||
}
|
||||
this.$refs.wrapper.innerHTML =
|
||||
'<svg id="svg-canvas" width="100%" style="min-height:800px"/>';
|
||||
const g = new dagreD3.graphlib.Graph({
|
||||
compound: true,
|
||||
multigraph: true
|
||||
})
|
||||
.setGraph({})
|
||||
.setDefaultEdgeLabel(function() {
|
||||
return {};
|
||||
});
|
||||
methods: {
|
||||
toggleOrientation() {
|
||||
this.orientation = !this.orientation;
|
||||
localStorage.setItem(
|
||||
"topology-orientation",
|
||||
this.orientation ? 1 : 0
|
||||
);
|
||||
this.generateGraph();
|
||||
},
|
||||
getVirtualRootNode() {
|
||||
return this.filterGroup
|
||||
? {
|
||||
task: {
|
||||
id: this.filterGroup
|
||||
}
|
||||
}
|
||||
: undefined;
|
||||
},
|
||||
generateGraph() {
|
||||
this.filteredDataTree = this.getFilteredDataTree();
|
||||
this.virtualRootNode = this.getVirtualRootNode();
|
||||
// Create the input graph
|
||||
const arrowColor = "#ccc";
|
||||
if (this.zoom) {
|
||||
this.zoom.on("zoom", null);
|
||||
}
|
||||
this.$refs.wrapper.innerHTML =
|
||||
"<svg id=\"svg-canvas\" width=\"100%\" style=\"min-height:800px\"/>";
|
||||
const g = new dagreD3.graphlib.Graph({
|
||||
compound: true,
|
||||
multigraph: true
|
||||
})
|
||||
.setGraph({})
|
||||
.setDefaultEdgeLabel(function() {
|
||||
return {};
|
||||
});
|
||||
|
||||
const getOptions = node => {
|
||||
const edgeOption = {};
|
||||
if (node.relation !== "SEQUENTIAL") {
|
||||
edgeOption.label = node.relation.toLowerCase();
|
||||
if (node.taskRun && node.taskRun.value) {
|
||||
edgeOption.label += ` : ${node.taskRun.value}`;
|
||||
const getOptions = node => {
|
||||
const edgeOption = {};
|
||||
if (node.relation !== "SEQUENTIAL") {
|
||||
edgeOption.label = node.relation.toLowerCase();
|
||||
if (node.taskRun && node.taskRun.value) {
|
||||
edgeOption.label += ` : ${node.taskRun.value}`;
|
||||
}
|
||||
}
|
||||
edgeOption.class =
|
||||
{
|
||||
ERROR: "error-edge",
|
||||
DYNAMIC: "dynamic-edge",
|
||||
CHOICE: "choice-edge",
|
||||
PARALLEL: "choice-edge"
|
||||
}[node.relation] || "";
|
||||
return edgeOption;
|
||||
};
|
||||
const ancestorsHashes = new Set(
|
||||
this.filteredDataTree.filter(e => e.parent).map(e => nodeHash(e.parent[0]))
|
||||
);
|
||||
const virtualRootId = this.getVirtualRootNode() && this.getVirtualRootNode().task.id
|
||||
for (const node of this.filteredDataTree) {
|
||||
const slug = this.slug(node);
|
||||
const hash = parentHash(node);
|
||||
g.setNode(slug, {
|
||||
labelType: "html",
|
||||
label: `<div class="node-binder" id="node-${slug}"/>`
|
||||
});
|
||||
const options = getOptions(node);
|
||||
const parentId = node.parent && node.parent[0] && node.parent[0].id
|
||||
if (ancestorsHashes.has(hash) && ["SEQUENTIAL", "ERROR"].includes(node.relation) && virtualRootId !== parentId) {
|
||||
g.setEdge(parentHash(node), slug, options);
|
||||
} else {
|
||||
g.setEdge("parent node", slug, options)
|
||||
}
|
||||
}
|
||||
edgeOption.class =
|
||||
{
|
||||
ERROR: "error-edge",
|
||||
DYNAMIC: "dynamic-edge",
|
||||
CHOICE: "choice-edge",
|
||||
PARALLEL: "choice-edge"
|
||||
}[node.relation] || "";
|
||||
return edgeOption;
|
||||
};
|
||||
const ancestorsHashes = new Set(
|
||||
this.filteredDataTree.filter(e => e.parent).map(e => nodeHash(e.parent[0]))
|
||||
);
|
||||
const virtualRootId = this.getVirtualRootNode() && this.getVirtualRootNode().task.id
|
||||
for (const node of this.filteredDataTree) {
|
||||
const slug = this.slug(node);
|
||||
const hash = parentHash(node);
|
||||
g.setNode(slug, {
|
||||
const rootNode = {
|
||||
labelType: "html",
|
||||
label: `<div class="node-binder" id="node-${slug}"/>`
|
||||
});
|
||||
const options = getOptions(node);
|
||||
const parentId = node.parent && node.parent[0] && node.parent[0].id
|
||||
if (ancestorsHashes.has(hash) && ['SEQUENTIAL', 'ERROR'].includes(node.relation) && virtualRootId !== parentId) {
|
||||
g.setEdge(parentHash(node), slug, options);
|
||||
clusterLabelPos: "bottom"
|
||||
};
|
||||
if (this.filterGroup) {
|
||||
rootNode.label = `<div class="node-binder root-node-virtual" id="node-${this.slug(
|
||||
this.virtualRootNode
|
||||
)}"/>`;
|
||||
} else {
|
||||
g.setEdge("parent node", slug, options)
|
||||
rootNode.class = "root-node";
|
||||
rootNode.label = "<div class=\"vector-circle-wrapper\"/>";
|
||||
rootNode.height = 30;
|
||||
rootNode.width = 30;
|
||||
}
|
||||
}
|
||||
const rootNode = {
|
||||
labelType: "html",
|
||||
clusterLabelPos: "bottom"
|
||||
};
|
||||
if (this.filterGroup) {
|
||||
rootNode.label = `<div class="node-binder root-node-virtual" id="node-${this.slug(
|
||||
this.virtualRootNode
|
||||
)}"/>`;
|
||||
} else {
|
||||
rootNode.class = "root-node";
|
||||
rootNode.label = '<div class="vector-circle-wrapper"/>';
|
||||
rootNode.height = 30;
|
||||
rootNode.width = 30;
|
||||
}
|
||||
g.setNode("parent node", rootNode);
|
||||
g.nodes().forEach(v => {
|
||||
const node = g.node(v);
|
||||
if (node) {
|
||||
node.paddingLeft = node.paddingRight = node.paddingTop = node.paddingBottom = 0;
|
||||
g.setNode("parent node", rootNode);
|
||||
g.nodes().forEach(v => {
|
||||
const node = g.node(v);
|
||||
if (node) {
|
||||
node.paddingLeft = node.paddingRight = node.paddingTop = node.paddingBottom = 0;
|
||||
}
|
||||
});
|
||||
if (!this.orientation) {
|
||||
g.graph().rankDir = "LR";
|
||||
}
|
||||
});
|
||||
if (!this.orientation) {
|
||||
g.graph().rankDir = "LR";
|
||||
}
|
||||
const render = new dagreD3.render();
|
||||
// Set up an SVG group so that we can translate the final graph.
|
||||
const svgWrapper = d3.select("#svg-canvas"),
|
||||
svgGroup = svgWrapper.append("g");
|
||||
// Run the renderer. This is what draws the final graph.
|
||||
this.zoom = d3
|
||||
.zoom()
|
||||
.on("zoom", () => {
|
||||
const t = d3.event.transform;
|
||||
svgGroup.attr(
|
||||
"transform",
|
||||
`translate(${t.x},${t.y}) scale(${t.k})`
|
||||
);
|
||||
})
|
||||
.scaleExtent([1, 1]);
|
||||
svgWrapper.call(this.zoom);
|
||||
svgWrapper.on("dblclick.zoom", null);
|
||||
|
||||
render(d3.select("#svg-canvas g"), g);
|
||||
d3.selectAll("#svg-canvas g path").style("stroke", arrowColor);
|
||||
d3.selectAll("#svg-canvas .edgePath marker").style(
|
||||
"fill",
|
||||
arrowColor
|
||||
);
|
||||
const transform = d3.zoomIdentity.translate(50, 50).translate(0, 0);
|
||||
svgWrapper.call(this.zoom.transform, transform);
|
||||
this.bindNodes();
|
||||
},
|
||||
virtalNodeReady() {
|
||||
if (this.virtualRootNode) {
|
||||
const vueNode = this.$refs[
|
||||
`node-${this.slug(this.virtualRootNode)}`
|
||||
];
|
||||
return vueNode && vueNode.$el;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
bindNodes() {
|
||||
let ready = true;
|
||||
for (const node of this.filteredDataTree) {
|
||||
if (
|
||||
!this.virtalNodeReady() ||
|
||||
!this.$refs[this.slug(node)] ||
|
||||
!this.$refs[this.slug(node)].length ||
|
||||
!this.$refs["vector-circle"]
|
||||
) {
|
||||
ready = false;
|
||||
}
|
||||
}
|
||||
if (ready) {
|
||||
for (const node of this.filteredDataTree) {
|
||||
this.$el
|
||||
.querySelector(`#node-${this.slug(node)}`)
|
||||
.appendChild(this.$refs[this.slug(node)][0].$el);
|
||||
}
|
||||
if (this.virtualRootNode) {
|
||||
this.$el
|
||||
.querySelector(
|
||||
`#node-${this.slug(this.virtualRootNode)}`
|
||||
)
|
||||
.appendChild(
|
||||
this.$refs[
|
||||
`node-${this.slug(this.virtualRootNode)}`
|
||||
].$el
|
||||
const render = new dagreD3.render();
|
||||
// Set up an SVG group so that we can translate the final graph.
|
||||
const svgWrapper = d3.select("#svg-canvas"),
|
||||
svgGroup = svgWrapper.append("g");
|
||||
// Run the renderer. This is what draws the final graph.
|
||||
this.zoom = d3
|
||||
.zoom()
|
||||
.on("zoom", () => {
|
||||
const t = d3.event.transform;
|
||||
svgGroup.attr(
|
||||
"transform",
|
||||
`translate(${t.x},${t.y}) scale(${t.k})`
|
||||
);
|
||||
} else {
|
||||
this.$el
|
||||
.querySelector(".vector-circle-wrapper")
|
||||
.appendChild(this.$refs["vector-circle"]);
|
||||
}
|
||||
this.ready = true;
|
||||
} else {
|
||||
setTimeout(this.bindNodes, 30);
|
||||
}
|
||||
},
|
||||
onFilterGroup(group) {
|
||||
if (this.$route.query.filter != group) {
|
||||
this.filterGroup = group;
|
||||
this.$router.push({
|
||||
query: { ...this.$route.query, filter: group }
|
||||
});
|
||||
this.generateGraph();
|
||||
}
|
||||
},
|
||||
slug(node) {
|
||||
const hash =
|
||||
node.task.id +
|
||||
(node.taskRun && node.taskRun.value
|
||||
? "-" + node.taskRun.value
|
||||
: "");
|
||||
return hash.hashCode();
|
||||
},
|
||||
getFilteredDataTree() {
|
||||
if (this.filterGroup) {
|
||||
return this.dataTree.filter(
|
||||
node =>
|
||||
node.groups &&
|
||||
node.groups[node.groups.length - 1] === this.filterGroup
|
||||
})
|
||||
.scaleExtent([1, 1]);
|
||||
svgWrapper.call(this.zoom);
|
||||
svgWrapper.on("dblclick.zoom", null);
|
||||
|
||||
render(d3.select("#svg-canvas g"), g);
|
||||
d3.selectAll("#svg-canvas g path").style("stroke", arrowColor);
|
||||
d3.selectAll("#svg-canvas .edgePath marker").style(
|
||||
"fill",
|
||||
arrowColor
|
||||
);
|
||||
} else {
|
||||
return this.dataTree.filter(node => !node.groups);
|
||||
const transform = d3.zoomIdentity.translate(50, 50).translate(0, 0);
|
||||
svgWrapper.call(this.zoom.transform, transform);
|
||||
this.bindNodes();
|
||||
},
|
||||
virtalNodeReady() {
|
||||
if (this.virtualRootNode) {
|
||||
const vueNode = this.$refs[
|
||||
`node-${this.slug(this.virtualRootNode)}`
|
||||
];
|
||||
return vueNode && vueNode.$el;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
bindNodes() {
|
||||
let ready = true;
|
||||
for (const node of this.filteredDataTree) {
|
||||
if (
|
||||
!this.virtalNodeReady() ||
|
||||
!this.$refs[this.slug(node)] ||
|
||||
!this.$refs[this.slug(node)].length ||
|
||||
!this.$refs["vector-circle"]
|
||||
) {
|
||||
ready = false;
|
||||
}
|
||||
}
|
||||
if (ready) {
|
||||
for (const node of this.filteredDataTree) {
|
||||
this.$el
|
||||
.querySelector(`#node-${this.slug(node)}`)
|
||||
.appendChild(this.$refs[this.slug(node)][0].$el);
|
||||
}
|
||||
if (this.virtualRootNode) {
|
||||
this.$el
|
||||
.querySelector(
|
||||
`#node-${this.slug(this.virtualRootNode)}`
|
||||
)
|
||||
.appendChild(
|
||||
this.$refs[
|
||||
`node-${this.slug(this.virtualRootNode)}`
|
||||
].$el
|
||||
);
|
||||
} else {
|
||||
this.$el
|
||||
.querySelector(".vector-circle-wrapper")
|
||||
.appendChild(this.$refs["vector-circle"]);
|
||||
}
|
||||
this.ready = true;
|
||||
} else {
|
||||
setTimeout(this.bindNodes, 30);
|
||||
}
|
||||
},
|
||||
onFilterGroup(group) {
|
||||
if (this.$route.query.filter != group) {
|
||||
this.filterGroup = group;
|
||||
this.$router.push({
|
||||
query: {...this.$route.query, filter: group}
|
||||
});
|
||||
this.generateGraph();
|
||||
}
|
||||
},
|
||||
slug(node) {
|
||||
const hash =
|
||||
node.task.id +
|
||||
(node.taskRun && node.taskRun.value
|
||||
? "-" + node.taskRun.value
|
||||
: "");
|
||||
return hash.hashCode();
|
||||
},
|
||||
getFilteredDataTree() {
|
||||
if (this.filterGroup) {
|
||||
return this.dataTree.filter(
|
||||
node =>
|
||||
node.groups &&
|
||||
node.groups[node.groups.length - 1] === this.filterGroup
|
||||
);
|
||||
} else {
|
||||
return this.dataTree.filter(node => !node.groups);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
groups() {
|
||||
const groups = new Set();
|
||||
this.dataTree.forEach(node =>
|
||||
(node.groups || []).forEach(group => groups.add(group))
|
||||
);
|
||||
return groups;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.ready = false;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
groups() {
|
||||
const groups = new Set();
|
||||
this.dataTree.forEach(node =>
|
||||
(node.groups || []).forEach(group => groups.add(group))
|
||||
);
|
||||
return groups;
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
this.ready = false;
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import "../../styles/variable";
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
<template>
|
||||
<div class="node-wrapper">
|
||||
<div class="status-color" v-if="n.taskRun" :class="contentCls"></div>
|
||||
<div class="status-color" v-if="n.taskRun" :class="contentCls" />
|
||||
<div class="task-content">
|
||||
<div class="card-header">
|
||||
<div class="icon-wrapper">
|
||||
<!-- <img src=""/> -->
|
||||
<!-- <img src=""/> -->
|
||||
</div>
|
||||
<div class="task-title">
|
||||
<span>{{task.id | ellipsis(18)}}</span>
|
||||
<span>{{ task.id | ellipsis(18) }}</span>
|
||||
</div>
|
||||
<!-- <menu-open class="node-action" @click="onSettings" /> -->
|
||||
<!-- <menu-open class="node-action" @click="onSettings" /> -->
|
||||
</div>
|
||||
<div v-if="task.state" class="status-wrapper">
|
||||
<status :status="state" />
|
||||
@@ -82,104 +82,104 @@
|
||||
</div>
|
||||
|
||||
<b-modal size="xl" :hide-footer="true" :id="`modal-${hash}`" :title="`Task ${task.id}`">
|
||||
<pre>{{taskYaml}}</pre>
|
||||
<pre>{{ taskYaml }}</pre>
|
||||
</b-modal>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Console from "vue-material-design-icons/Console";
|
||||
import Graph from "vue-material-design-icons/Graph";
|
||||
import CodeTags from "vue-material-design-icons/CodeTags";
|
||||
import FormatListChecks from "vue-material-design-icons/FormatListChecks";
|
||||
import LocationExit from "vue-material-design-icons/LocationExit";
|
||||
import CurrentAc from "vue-material-design-icons/CurrentAc";
|
||||
import { mapState } from "vuex";
|
||||
import Status from "../Status";
|
||||
import md5 from "md5";
|
||||
import YamlUtils from "../../utils/yamlUtils";
|
||||
import MarkdownTooltip from "../../components/layout/MarkdownTooltip";
|
||||
import Console from "vue-material-design-icons/Console";
|
||||
import Graph from "vue-material-design-icons/Graph";
|
||||
import CodeTags from "vue-material-design-icons/CodeTags";
|
||||
import FormatListChecks from "vue-material-design-icons/FormatListChecks";
|
||||
import LocationExit from "vue-material-design-icons/LocationExit";
|
||||
import CurrentAc from "vue-material-design-icons/CurrentAc";
|
||||
import {mapState} from "vuex";
|
||||
import Status from "../Status";
|
||||
import md5 from "md5";
|
||||
import YamlUtils from "../../utils/yamlUtils";
|
||||
import MarkdownTooltip from "../../components/layout/MarkdownTooltip";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MarkdownTooltip,
|
||||
Status,
|
||||
Console,
|
||||
Graph,
|
||||
CodeTags,
|
||||
FormatListChecks,
|
||||
LocationExit,
|
||||
CurrentAc
|
||||
},
|
||||
props: {
|
||||
n: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
export default {
|
||||
components: {
|
||||
MarkdownTooltip,
|
||||
Status,
|
||||
Console,
|
||||
Graph,
|
||||
CodeTags,
|
||||
FormatListChecks,
|
||||
LocationExit,
|
||||
CurrentAc
|
||||
},
|
||||
isFlow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
taskRunOutputToken(taskRun) {
|
||||
return md5(taskRun.taskId + (taskRun.value ? ` - ${taskRun.value}`: ''));
|
||||
},
|
||||
onFilterGroup() {
|
||||
this.$emit("onFilterGroup", this.task.id);
|
||||
},
|
||||
onSettings() {
|
||||
if (this.node) {
|
||||
this.$store.dispatch("graph/setNode", undefined);
|
||||
} else {
|
||||
this.$store.dispatch("graph/setNode", this.n);
|
||||
this.$emit("onSettings");
|
||||
props: {
|
||||
n: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
},
|
||||
isFlow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("graph", ["node"]),
|
||||
hasLogs() {
|
||||
// @TODO
|
||||
return true;
|
||||
},
|
||||
methods: {
|
||||
taskRunOutputToken(taskRun) {
|
||||
return md5(taskRun.taskId + (taskRun.value ? ` - ${taskRun.value}`: ""));
|
||||
},
|
||||
onFilterGroup() {
|
||||
this.$emit("onFilterGroup", this.task.id);
|
||||
},
|
||||
onSettings() {
|
||||
if (this.node) {
|
||||
this.$store.dispatch("graph/setNode", undefined);
|
||||
} else {
|
||||
this.$store.dispatch("graph/setNode", this.n);
|
||||
this.$emit("onSettings");
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("graph", ["node"]),
|
||||
hasLogs() {
|
||||
// @TODO
|
||||
return true;
|
||||
// return (
|
||||
// this.attempts.filter(attempt => attempt.logs.length).length > 0
|
||||
// );
|
||||
},
|
||||
hasOutputs() {
|
||||
return this.n.taskRun && this.n.taskRun.outputs;
|
||||
},
|
||||
attempts() {
|
||||
return this.n.taskRun && this.n.taskRun.attempts
|
||||
? this.n.taskRun.attempts
|
||||
: [];
|
||||
},
|
||||
hash() {
|
||||
return this.task.id.hashCode();
|
||||
},
|
||||
childrenCount() {
|
||||
return this.task.tasks ? this.task.tasks.length : 0;
|
||||
},
|
||||
taskYaml() {
|
||||
return YamlUtils.stringify(this.n);
|
||||
},
|
||||
state() {
|
||||
return this.n.taskRun ? this.n.taskRun.state.current : "SUCCESS";
|
||||
},
|
||||
contentCls() {
|
||||
return {
|
||||
"is-success": !["RUNNING", "FAILED"].includes(this.state),
|
||||
"is-running": this.state === "RUNNING",
|
||||
"is-failed": this.state === "FAILED"
|
||||
};
|
||||
},
|
||||
task() {
|
||||
return this.n.task;
|
||||
},
|
||||
value () {
|
||||
return this.n.taskRun && this.n.taskRun.value
|
||||
},
|
||||
hasOutputs() {
|
||||
return this.n.taskRun && this.n.taskRun.outputs;
|
||||
},
|
||||
attempts() {
|
||||
return this.n.taskRun && this.n.taskRun.attempts
|
||||
? this.n.taskRun.attempts
|
||||
: [];
|
||||
},
|
||||
hash() {
|
||||
return this.task.id.hashCode();
|
||||
},
|
||||
childrenCount() {
|
||||
return this.task.tasks ? this.task.tasks.length : 0;
|
||||
},
|
||||
taskYaml() {
|
||||
return YamlUtils.stringify(this.n);
|
||||
},
|
||||
state() {
|
||||
return this.n.taskRun ? this.n.taskRun.state.current : "SUCCESS";
|
||||
},
|
||||
contentCls() {
|
||||
return {
|
||||
"is-success": !["RUNNING", "FAILED"].includes(this.state),
|
||||
"is-running": this.state === "RUNNING",
|
||||
"is-failed": this.state === "FAILED"
|
||||
};
|
||||
},
|
||||
task() {
|
||||
return this.n.task;
|
||||
},
|
||||
value () {
|
||||
return this.n.taskRun && this.n.taskRun.value
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import "../../styles/_variable.scss";
|
||||
|
||||
@@ -3,11 +3,15 @@
|
||||
<b-navbar type="dark" variant="dark">
|
||||
<b-btn-group>
|
||||
<b-button @click="autoFold(true)" size="sm" variant="light" v-b-tooltip.hover.top="$t('Fold content lines')">
|
||||
<unfold-less-horizontal/>
|
||||
<unfold-less-horizontal />
|
||||
</b-button>
|
||||
<b-button @click="unfoldAll" size="sm" variant="light"
|
||||
v-b-tooltip.hover.top="$t('Unfold content lines')">
|
||||
<unfold-more-horizontal/>
|
||||
<b-button
|
||||
@click="unfoldAll"
|
||||
size="sm"
|
||||
variant="light"
|
||||
v-b-tooltip.hover.top="$t('Unfold content lines')"
|
||||
>
|
||||
<unfold-more-horizontal />
|
||||
</b-button>
|
||||
</b-btn-group>
|
||||
</b-navbar>
|
||||
@@ -19,9 +23,9 @@
|
||||
:lang="lang"
|
||||
theme="merbivore_soft"
|
||||
:width="width"
|
||||
minLines="5"
|
||||
min-lines="5"
|
||||
:height="height"
|
||||
></editor>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -29,7 +33,7 @@
|
||||
import UnfoldLessHorizontal from "vue-material-design-icons/UnfoldLessHorizontal";
|
||||
import UnfoldMoreHorizontal from "vue-material-design-icons/UnfoldMoreHorizontal";
|
||||
|
||||
import YamlUtils from '../../utils/yamlUtils';
|
||||
import YamlUtils from "../../utils/yamlUtils";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
@@ -60,7 +64,7 @@
|
||||
editor.setOptions({
|
||||
minLines: 5,
|
||||
maxLines: Infinity,
|
||||
fontFamily: '"Source Code Pro", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
||||
fontFamily: "\"Source Code Pro\", SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace",
|
||||
showPrintMargin: false,
|
||||
tabSize: 2,
|
||||
wrap: false,
|
||||
@@ -73,15 +77,15 @@
|
||||
name: "save",
|
||||
bindKey: {win: "Ctrl-S", mac: "Cmd-S"},
|
||||
exec: (editor) => {
|
||||
this.$emit('onSave', editor.session.getValue())
|
||||
this.$emit("onSave", editor.session.getValue())
|
||||
},
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.autoFold(localStorage.getItem('autofoldTextEditor') === "1")
|
||||
this.autoFold(localStorage.getItem("autofoldTextEditor") === "1")
|
||||
})
|
||||
},
|
||||
trimContent(text) {
|
||||
return text.split('\n').map(line => line.trim()).join('\n')
|
||||
return text.split("\n").map(line => line.trim()).join("\n")
|
||||
},
|
||||
autoFold(autoFold) {
|
||||
//we may add try in case content is not serializable a json
|
||||
@@ -92,8 +96,8 @@
|
||||
for (const foldableToken of foldableTokens) {
|
||||
const search = this.trimContent(foldableToken)
|
||||
const index = trimmedContent.indexOf(search)
|
||||
const line = trimmedContent.slice(0, index).split('\n').length + lineDiff
|
||||
lineDiff = line + search.split('\n').length - 2
|
||||
const line = trimmedContent.slice(0, index).split("\n").length + lineDiff
|
||||
lineDiff = line + search.split("\n").length - 2
|
||||
trimmedContent = trimmedContent.slice(index + search.length)
|
||||
this.ed.getSession().$toggleFoldWidget(line - 2, {})
|
||||
}
|
||||
@@ -103,12 +107,12 @@
|
||||
for (const n of node) {
|
||||
this.getFoldLines(n, foldableTokens, autoFold)
|
||||
}
|
||||
} else if (typeof (node) === 'object') {
|
||||
} else if (typeof (node) === "object") {
|
||||
for (const key in node) {
|
||||
this.getFoldLines(node[key], foldableTokens, autoFold)
|
||||
}
|
||||
} else if (typeof (node) === 'string') {
|
||||
if (node.split('\n').length > 1 && autoFold) {
|
||||
} else if (typeof (node) === "string") {
|
||||
if (node.split("\n").length > 1 && autoFold) {
|
||||
foldableTokens.push(node)
|
||||
}
|
||||
}
|
||||
@@ -117,7 +121,7 @@
|
||||
this.ed.getSession().expandFolds(this.ed.getSession().getAllFolds())
|
||||
},
|
||||
onInput(value) {
|
||||
this.$emit('input', value);
|
||||
this.$emit("input", value);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<b-navbar class="bottom-line" fixed="bottom" toggleable="lg">
|
||||
<slot/>
|
||||
<slot />
|
||||
</b-navbar>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@@ -1,40 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-navbar toggleable="lg" type="light" variant="light" v-if="hasNavBar">
|
||||
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
|
||||
<b-navbar-toggle target="nav-collapse" />
|
||||
<b-collapse id="nav-collapse" is-nav>
|
||||
<b-nav-form>
|
||||
<slot name="navbar"></slot>
|
||||
<slot name="navbar" />
|
||||
</b-nav-form>
|
||||
</b-collapse>
|
||||
</b-navbar>
|
||||
|
||||
<slot name="top"></slot>
|
||||
<slot name="top" />
|
||||
|
||||
<slot name="table"></slot>
|
||||
<slot name="table" />
|
||||
<pagination :total="total" :max="max" @onPageChanged="onPageChanged" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Pagination from "./Pagination";
|
||||
export default {
|
||||
components: { Pagination },
|
||||
computed: {
|
||||
hasNavBar() {
|
||||
return !!this.$slots["navbar"];
|
||||
import Pagination from "./Pagination";
|
||||
export default {
|
||||
components: {Pagination},
|
||||
computed: {
|
||||
hasNavBar() {
|
||||
return !!this.$slots["navbar"];
|
||||
},
|
||||
},
|
||||
},
|
||||
props: {
|
||||
total: { type: Number, required: true },
|
||||
max: {type: Number, required:false}
|
||||
},
|
||||
methods: {
|
||||
onPageChanged(pagination) {
|
||||
this.$emit("onPageChanged", pagination);
|
||||
props: {
|
||||
total: {type: Number, required: true},
|
||||
max: {type: Number, required:false, default: undefined}},
|
||||
methods: {
|
||||
onPageChanged(pagination) {
|
||||
this.$emit("onPageChanged", pagination);
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
type="datetime"
|
||||
class="sm"
|
||||
:placeholder="$t('start datetime')"
|
||||
></date-picker>
|
||||
/>
|
||||
<date-picker
|
||||
@input="onDate"
|
||||
v-model="end"
|
||||
@@ -15,43 +15,43 @@
|
||||
type="datetime"
|
||||
class="sm"
|
||||
:placeholder="$t('end datetime')"
|
||||
></date-picker>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import DatePicker from "vue2-datepicker";
|
||||
export default {
|
||||
components: { DatePicker },
|
||||
data() {
|
||||
return {
|
||||
start: null,
|
||||
end: null
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.start) {
|
||||
this.start = new Date(parseInt(this.$route.query.start));
|
||||
}
|
||||
if (this.$route.query.end) {
|
||||
this.end = new Date(parseInt(this.$route.query.end));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDate() {
|
||||
const start = this.start,
|
||||
end = this.end;
|
||||
const dateRange = {
|
||||
start: start ? start.toISOString() : null,
|
||||
end: end ? end.toISOString() : null
|
||||
import DatePicker from "vue2-datepicker";
|
||||
export default {
|
||||
components: {DatePicker},
|
||||
data() {
|
||||
return {
|
||||
start: null,
|
||||
end: null
|
||||
};
|
||||
const query = { ...this.$route.query };
|
||||
query.start = start ? start.getTime() : undefined;
|
||||
query.end = end ? end.getTime() : undefined;
|
||||
this.$router.push({ query });
|
||||
this.$emit("onDate", dateRange);
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.start) {
|
||||
this.start = new Date(parseInt(this.$route.query.start));
|
||||
}
|
||||
if (this.$route.query.end) {
|
||||
this.end = new Date(parseInt(this.$route.query.end));
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDate() {
|
||||
const start = this.start,
|
||||
end = this.end;
|
||||
const dateRange = {
|
||||
start: start ? start.toISOString() : null,
|
||||
end: end ? end.toISOString() : null
|
||||
};
|
||||
const query = {...this.$route.query};
|
||||
query.start = start ? start.getTime() : undefined;
|
||||
query.end = end ? end.getTime() : undefined;
|
||||
this.$router.push({query});
|
||||
this.$emit("onDate", dateRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.time-line {
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<span v-html="markdownRenderer"></span>
|
||||
<span v-html="markdownRenderer" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Prism from "prismjs";
|
||||
import "prismjs/themes/prism-okaidia.css";
|
||||
import 'prismjs/components/prism-yaml.min';
|
||||
import "prismjs/components/prism-yaml.min";
|
||||
import Markdown from "../../utils/markdown";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
watches: {
|
||||
type: Array,
|
||||
default: () => ['source', 'show', 'toc'],
|
||||
default: () => ["source", "show", "toc"],
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
default: ``,
|
||||
default: "",
|
||||
},
|
||||
permalink: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
markdownRenderer() {
|
||||
const outHtml = Markdown.render(this.source, {permalink: this.permalink});
|
||||
const outHtml = Markdown.render(this.source, {
|
||||
permalink: this.permalink,
|
||||
});
|
||||
|
||||
this.$emit('rendered', outHtml)
|
||||
this.$emit("rendered", outHtml);
|
||||
|
||||
this.$nextTick(() => {
|
||||
Prism.highlightAll();
|
||||
@@ -39,7 +41,6 @@
|
||||
return outHtml;
|
||||
},
|
||||
},
|
||||
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -52,5 +53,4 @@
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<span v-if="description">
|
||||
<help-circle title="" :id="'tooltip-desc-' + id" />
|
||||
<b-popover triggers="hover" :target="'tooltip-desc-' + id" placement="bottom">
|
||||
<markdown :source="description"></markdown>
|
||||
<markdown :source="description" />
|
||||
</b-popover>
|
||||
</span>
|
||||
</template>
|
||||
@@ -22,7 +22,7 @@
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
default: ``,
|
||||
default: "",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
@change="pageSizeChange"
|
||||
size="sm"
|
||||
:options="pageOptions"
|
||||
></b-form-select>
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<b-pagination
|
||||
@@ -18,49 +18,50 @@
|
||||
size="sm"
|
||||
class="my-0"
|
||||
align="right"
|
||||
></b-pagination>
|
||||
/>
|
||||
</div>
|
||||
<small v-if="max" class="btn btn-sm btn-outline-light text-muted"
|
||||
>{{ $t('Max displayed') }}: {{ max }}</small
|
||||
>
|
||||
<small class="btn btn-sm btn-outline-light text-muted"
|
||||
>{{ $t('Total') }}: {{ total }}</small
|
||||
>
|
||||
|
||||
<small v-if="max" class="btn btn-sm btn-outline-light text-muted">
|
||||
{{ $t('Max displayed') }}: {{ max }}
|
||||
</small>
|
||||
|
||||
<small class="btn btn-sm btn-outline-light text-muted">
|
||||
{{ $t('Total') }}: {{ total }}
|
||||
</small>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
total: { type: Number, required: true },
|
||||
max: {type: Number, required:false}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
size: parseInt(this.$route.query.size || 25),
|
||||
page: parseInt(this.$route.query.page || 1),
|
||||
pageOptions: [
|
||||
{ value: 10, text: `10 ${this.$t("Per page")}` },
|
||||
{ value: 25, text: `25 ${this.$t("Per page")}` },
|
||||
{ value: 50, text: `50 ${this.$t("Per page")}` },
|
||||
{ value: 100, text: `100 ${this.$t("Per page")}` },
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
pageSizeChange() {
|
||||
this.$emit("onPageChanged", {
|
||||
page: 1,
|
||||
size: this.size,
|
||||
});
|
||||
export default {
|
||||
props: {
|
||||
total: {type: Number, required: true},
|
||||
max: {type: Number, required:false, default: undefined}},
|
||||
data() {
|
||||
return {
|
||||
size: parseInt(this.$route.query.size || 25),
|
||||
page: parseInt(this.$route.query.page || 1),
|
||||
pageOptions: [
|
||||
{value: 10, text: `10 ${this.$t("Per page")}`},
|
||||
{value: 25, text: `25 ${this.$t("Per page")}`},
|
||||
{value: 50, text: `50 ${this.$t("Per page")}`},
|
||||
{value: 100, text: `100 ${this.$t("Per page")}`},
|
||||
],
|
||||
};
|
||||
},
|
||||
pageChanged(page) {
|
||||
this.$emit("onPageChanged", {
|
||||
page: page,
|
||||
size: this.size,
|
||||
});
|
||||
methods: {
|
||||
pageSizeChange() {
|
||||
this.$emit("onPageChanged", {
|
||||
page: 1,
|
||||
size: this.size,
|
||||
});
|
||||
},
|
||||
pageChanged(page) {
|
||||
this.$emit("onPageChanged", {
|
||||
page: page,
|
||||
size: this.size,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
select {
|
||||
|
||||
@@ -2,56 +2,60 @@
|
||||
<b-button-group size="sm">
|
||||
<b-tooltip
|
||||
target="toggle-automatic-refresh-action"
|
||||
>{{$t('toggle periodic refresh each 10 seconds')}}</b-tooltip>
|
||||
>
|
||||
{{ $t('toggle periodic refresh each 10 seconds') }}
|
||||
</b-tooltip>
|
||||
<b-button @click="toggleAutoRefresh" :pressed="autoRefresh" id="toggle-automatic-refresh-action">
|
||||
<clock/> <span class="label">{{$t('automatic refresh')}}</span>
|
||||
<clock /> <span class="label">{{ $t('automatic refresh') }}</span>
|
||||
</b-button>
|
||||
<b-tooltip target="trigger-refresh-action">{{ $t('trigger refresh') }}</b-tooltip>
|
||||
<b-tooltip target="trigger-refresh-action">
|
||||
{{ $t('trigger refresh') }}
|
||||
</b-tooltip>
|
||||
<b-button @click="triggerRefresh" id="trigger-refresh-action">
|
||||
<refresh/> <span class="label">{{ $t('trigger refresh') }}</span>
|
||||
<refresh /> <span class="label">{{ $t('trigger refresh') }}</span>
|
||||
</b-button>
|
||||
</b-button-group>
|
||||
</template>
|
||||
<script>
|
||||
import Refresh from "vue-material-design-icons/Refresh";
|
||||
import Clock from "vue-material-design-icons/Clock";
|
||||
import Refresh from "vue-material-design-icons/Refresh";
|
||||
import Clock from "vue-material-design-icons/Clock";
|
||||
|
||||
export default {
|
||||
components: { Refresh, Clock },
|
||||
data() {
|
||||
return {
|
||||
autoRefresh: false,
|
||||
refreshHandler: undefined
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.autoRefresh = localStorage.getItem("autoRefresh") === "1";
|
||||
},
|
||||
methods: {
|
||||
toggleAutoRefresh() {
|
||||
this.autoRefresh = !this.autoRefresh;
|
||||
localStorage.setItem("autoRefresh", this.autoRefresh ? "1" : "0");
|
||||
if (this.autoRefresh) {
|
||||
this.refreshHandler = setInterval(this.triggerRefresh, 10000);
|
||||
this.triggerRefresh()
|
||||
} else {
|
||||
this.stopRefresh();
|
||||
export default {
|
||||
components: {Refresh, Clock},
|
||||
data() {
|
||||
return {
|
||||
autoRefresh: false,
|
||||
refreshHandler: undefined
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.autoRefresh = localStorage.getItem("autoRefresh") === "1";
|
||||
},
|
||||
methods: {
|
||||
toggleAutoRefresh() {
|
||||
this.autoRefresh = !this.autoRefresh;
|
||||
localStorage.setItem("autoRefresh", this.autoRefresh ? "1" : "0");
|
||||
if (this.autoRefresh) {
|
||||
this.refreshHandler = setInterval(this.triggerRefresh, 10000);
|
||||
this.triggerRefresh()
|
||||
} else {
|
||||
this.stopRefresh();
|
||||
}
|
||||
},
|
||||
triggerRefresh() {
|
||||
this.$emit("onRefresh");
|
||||
},
|
||||
stopRefresh() {
|
||||
if (this.refreshHandler) {
|
||||
clearInterval(this.refreshHandler);
|
||||
this.refreshHandler = undefined
|
||||
}
|
||||
}
|
||||
},
|
||||
triggerRefresh() {
|
||||
this.$emit("onRefresh");
|
||||
},
|
||||
stopRefresh() {
|
||||
if (this.refreshHandler) {
|
||||
clearInterval(this.refreshHandler);
|
||||
this.refreshHandler = undefined
|
||||
}
|
||||
beforeDestroy() {
|
||||
this.stopRefresh();
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.stopRefresh();
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -6,37 +6,37 @@
|
||||
@input="onSearch"
|
||||
v-model="search"
|
||||
:placeholder="$t('search')"
|
||||
></b-form-input>
|
||||
/>
|
||||
</b-nav-form>
|
||||
</template>
|
||||
<script>
|
||||
import { debounce } from "throttle-debounce";
|
||||
export default {
|
||||
created() {
|
||||
if (this.$route.query.q) {
|
||||
this.search = this.$route.query.q;
|
||||
}
|
||||
this.searchDebounce = debounce(300, () => {
|
||||
this.$emit("onSearch", this.search);
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onSearch() {
|
||||
const query = { ...this.$route.query, q: this.search, page: 1 };
|
||||
if (!this.search) {
|
||||
delete query.q;
|
||||
import {debounce} from "throttle-debounce";
|
||||
export default {
|
||||
created() {
|
||||
if (this.$route.query.q) {
|
||||
this.search = this.$route.query.q;
|
||||
}
|
||||
this.$router.push({ query });
|
||||
this.searchDebounce();
|
||||
this.searchDebounce = debounce(300, () => {
|
||||
this.$emit("onSearch", this.search);
|
||||
});
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.searchDebounce.cancel();
|
||||
}
|
||||
};
|
||||
data() {
|
||||
return {
|
||||
search: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onSearch() {
|
||||
const query = {...this.$route.query, q: this.search, page: 1};
|
||||
if (!this.search) {
|
||||
delete query.q;
|
||||
}
|
||||
this.$router.push({query});
|
||||
this.searchDebounce();
|
||||
},
|
||||
},
|
||||
destroyed() {
|
||||
this.searchDebounce.cancel();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
<template>
|
||||
<b-form-select @input="searchStatus" v-model="selected" size="sm" :options="statuses"></b-form-select>
|
||||
<b-form-select @input="searchStatus" v-model="selected" size="sm" :options="statuses" />
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
statuses: ["all", "running", "success", "failed"],
|
||||
selected: "all"
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.status) {
|
||||
this.selected = this.$route.query.status.toLowerCase();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
searchStatus() {
|
||||
const status = this.selected.toUpperCase();
|
||||
if (this.$route.query.status !== status) {
|
||||
this.$router.push({ query: { ...this.$route.query, status } });
|
||||
this.$emit("onRefresh");
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
statuses: ["all", "running", "success", "failed"],
|
||||
selected: "all"
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.$route.query.status) {
|
||||
this.selected = this.$route.query.status.toLowerCase();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
searchStatus() {
|
||||
const status = this.selected.toUpperCase();
|
||||
if (this.$route.query.status !== status) {
|
||||
this.$router.push({query: {...this.$route.query, status}});
|
||||
this.$emit("onRefresh");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<template>
|
||||
<b-navbar v-if="topNavbar" :class="menuCollapsed" class="top-line" type="dark" variant="dark">
|
||||
<b-navbar-nav>
|
||||
<b-nav-text >
|
||||
<h1>{{title}}</h1>
|
||||
<b-nav-text>
|
||||
<h1>{{ title }}</h1>
|
||||
|
||||
<b-breadcrumb>
|
||||
<b-breadcrumb-item><router-link :to="{ name: 'home'}"><home /> {{$t('home')}}</router-link></b-breadcrumb-item>
|
||||
<b-breadcrumb-item>
|
||||
<router-link :to="{name: 'home'}">
|
||||
<home /> {{ $t('home') }}
|
||||
</router-link>
|
||||
</b-breadcrumb-item>
|
||||
<b-breadcrumb-item v-for="(item, x) in topNavbar.breadcrumb" :to="item.link" :text="item.label" :key="x" />
|
||||
</b-breadcrumb>
|
||||
</b-nav-text>
|
||||
@@ -14,31 +18,31 @@
|
||||
</b-navbar>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import Home from "vue-material-design-icons/Home";
|
||||
import Auth from "Override/components/auth/Auth";
|
||||
import {mapState} from "vuex";
|
||||
import Home from "vue-material-design-icons/Home";
|
||||
import Auth from "Override/components/auth/Auth";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Home,
|
||||
Auth,
|
||||
},
|
||||
props: {
|
||||
menuCollapsed : {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("layout", ["topNavbar"]),
|
||||
title() {
|
||||
return this.topNavbar.title;
|
||||
export default {
|
||||
components: {
|
||||
Home,
|
||||
Auth,
|
||||
},
|
||||
breadcrumb() {
|
||||
return this.topNavbar.breadcrumb.join(" > ");
|
||||
props: {
|
||||
menuCollapsed : {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState("layout", ["topNavbar"]),
|
||||
title() {
|
||||
return this.topNavbar.title;
|
||||
},
|
||||
breadcrumb() {
|
||||
return this.topNavbar.breadcrumb.join(" > ");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../styles/variable";
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
size="sm"
|
||||
@input="onChange"
|
||||
:placeholder="$t('search') + '...'"
|
||||
></b-form-input>
|
||||
/>
|
||||
</b-form-group>
|
||||
</b-col>
|
||||
<b-col md="6">
|
||||
@@ -26,20 +26,20 @@
|
||||
</b-row>
|
||||
</template>
|
||||
<script>
|
||||
import LogLevelSelector from "./LogLevelSelector";
|
||||
export default {
|
||||
components: { LogLevelSelector },
|
||||
data() {
|
||||
return {
|
||||
filter: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onChange() {
|
||||
const query = { ...this.$route.query, q: this.filter, page: 1 };
|
||||
this.$router.push({ query });
|
||||
this.$emit("onChange");
|
||||
import LogLevelSelector from "./LogLevelSelector";
|
||||
export default {
|
||||
components: {LogLevelSelector},
|
||||
data() {
|
||||
return {
|
||||
filter: "",
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
methods: {
|
||||
onChange() {
|
||||
const query = {...this.$route.query, q: this.filter, page: 1};
|
||||
this.$router.push({query});
|
||||
this.$emit("onChange");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -6,29 +6,29 @@
|
||||
size="sm"
|
||||
@input="onChange"
|
||||
:options="levelOptions"
|
||||
></b-form-select>
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
level: "INFO",
|
||||
levelOptions: [
|
||||
"TRACE",
|
||||
"DEBUG",
|
||||
"INFO",
|
||||
"WARN",
|
||||
"ERROR",
|
||||
"CRITICAL",
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onChange() {
|
||||
const query = { ...this.$route.query, level: this.level };
|
||||
this.$router.push({ query });
|
||||
this.$emit("onChange");
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
level: "INFO",
|
||||
levelOptions: [
|
||||
"TRACE",
|
||||
"DEBUG",
|
||||
"INFO",
|
||||
"WARN",
|
||||
"ERROR",
|
||||
"CRITICAL",
|
||||
],
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
methods: {
|
||||
onChange() {
|
||||
const query = {...this.$route.query, level: this.level};
|
||||
this.$router.push({query});
|
||||
this.$emit("onChange");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -16,60 +16,61 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
log: {
|
||||
type: Object,
|
||||
required: true,
|
||||
export default {
|
||||
props: {
|
||||
log: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
filter: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
level: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
excludeMetas: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
filter: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
level: {
|
||||
type: String,
|
||||
},
|
||||
excludeMetas: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
metaWithValue() {
|
||||
const metaWithValue = [];
|
||||
const excludes = [
|
||||
"message",
|
||||
"timestamp",
|
||||
"thread",
|
||||
"taskRunId",
|
||||
"level",
|
||||
];
|
||||
excludes.push.apply(excludes, this.excludeMetas);
|
||||
for (const key in this.log) {
|
||||
if (this.log[key] && !excludes.includes(key)) {
|
||||
metaWithValue.push({ key, value: this.log[key] });
|
||||
computed: {
|
||||
metaWithValue() {
|
||||
const metaWithValue = [];
|
||||
const excludes = [
|
||||
"message",
|
||||
"timestamp",
|
||||
"thread",
|
||||
"taskRunId",
|
||||
"level",
|
||||
];
|
||||
excludes.push.apply(excludes, this.excludeMetas);
|
||||
for (const key in this.log) {
|
||||
if (this.log[key] && !excludes.includes(key)) {
|
||||
metaWithValue.push({key, value: this.log[key]});
|
||||
}
|
||||
}
|
||||
}
|
||||
return metaWithValue;
|
||||
return metaWithValue;
|
||||
},
|
||||
levelClass() {
|
||||
return {
|
||||
TRACE: "badge-info",
|
||||
DEBUG: "badge-secondary",
|
||||
INFO: "badge-primary",
|
||||
WARN: "badge-warning",
|
||||
ERROR: "badge-danger",
|
||||
CRITICAL: "badge-danger font-weight-bold",
|
||||
}[this.log.level];
|
||||
},
|
||||
filtered() {
|
||||
return (
|
||||
this.log.message &&
|
||||
this.log.message.toLowerCase().includes(this.filter)
|
||||
);
|
||||
},
|
||||
},
|
||||
levelClass() {
|
||||
return {
|
||||
TRACE: "badge-info",
|
||||
DEBUG: "badge-secondary",
|
||||
INFO: "badge-primary",
|
||||
WARN: "badge-warning",
|
||||
ERROR: "badge-danger",
|
||||
CRITICAL: "badge-danger font-weight-bold",
|
||||
}[this.log.level];
|
||||
},
|
||||
filtered() {
|
||||
return (
|
||||
this.log.message &&
|
||||
this.log.message.toLowerCase().includes(this.filter)
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@import "../../styles/_variable.scss";
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div v-if="execution" class="log-wrapper text-white">
|
||||
<div v-for="taskItem in execution.taskRunList" :key="taskItem.id">
|
||||
<template v-if="(!task || task.id === taskItem.id) && taskItem.attempts">
|
||||
<template
|
||||
v-if="(!task || task.id === taskItem.id) && taskItem.attempts"
|
||||
>
|
||||
<div class="bg-dark attempt-wrapper">
|
||||
<template v-for="(attempt, index) in taskItem.attempts">
|
||||
<div
|
||||
@@ -13,41 +15,55 @@
|
||||
:target="`attempt-${index}-${taskItem.id}`"
|
||||
triggers="hover"
|
||||
>
|
||||
{{ $t('from') }} : {{ attempt.state.startDate | date('LLL:ss') }}
|
||||
<br/>
|
||||
{{ $t('to') }} : {{ attempt.state.endDate | date('LLL:ss') }}
|
||||
<br/>
|
||||
<br/>
|
||||
<clock/>
|
||||
{{ $t('duration') }} : {{ attempt.state.duration | humanizeDuration }}
|
||||
{{ $t("from") }} :
|
||||
{{ attempt.state.startDate | date("LLL:ss") }}
|
||||
<br>
|
||||
{{ $t("to") }} :
|
||||
{{ attempt.state.endDate | date("LLL:ss") }}
|
||||
<br>
|
||||
<br>
|
||||
<clock />
|
||||
{{ $t("duration") }} :
|
||||
{{ attempt.state.duration | humanizeDuration }}
|
||||
</b-tooltip>
|
||||
|
||||
<div class="attempt-header">
|
||||
<div class="attempt-number mr-1">
|
||||
{{ $t('attempt') }} {{ index + 1 }}
|
||||
{{ $t("attempt") }} {{ index + 1 }}
|
||||
</div>
|
||||
|
||||
<div class="task-id flex-grow-1">
|
||||
<code>{{ taskItem.taskId }}</code>
|
||||
<small v-if="taskItem.value"> {{ taskItem.value }}</small>
|
||||
<small v-if="taskItem.value">
|
||||
{{ taskItem.value }}</small>
|
||||
</div>
|
||||
|
||||
<b-button-group>
|
||||
<b-button
|
||||
v-if="taskItem.outputs"
|
||||
:title="$t('toggle metrics')"
|
||||
@click="toggleShowMetric(taskItem, index)"
|
||||
><chart-areaspline :title="$t('toggle metrics')" /></b-button>
|
||||
@click="
|
||||
toggleShowMetric(taskItem, index)
|
||||
"
|
||||
>
|
||||
<chart-areaspline
|
||||
:title="$t('toggle metrics')"
|
||||
/>
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
v-if="taskItem.outputs"
|
||||
:title="$t('toggle output')"
|
||||
@click="toggleShowOutput(taskItem)"
|
||||
><location-exit :title="$t('toggle output')" /></b-button>
|
||||
>
|
||||
<location-exit
|
||||
:title="$t('toggle output')"
|
||||
/>
|
||||
</b-button>
|
||||
|
||||
<restart
|
||||
:key="`restart-${index}-${attempt.state.startDate}`"
|
||||
:isButtonGroup="true"
|
||||
:is-button-group="true"
|
||||
:execution="execution"
|
||||
:task="taskItem"
|
||||
/>
|
||||
@@ -57,7 +73,9 @@
|
||||
|
||||
<!-- Log lines -->
|
||||
<template>
|
||||
<template v-for="(log, i) in findLogs(taskItem.id, index)">
|
||||
<template
|
||||
v-for="(log, i) in findLogs(taskItem.id, index)"
|
||||
>
|
||||
<log-line
|
||||
:level="level"
|
||||
:filter="filter"
|
||||
@@ -74,8 +92,8 @@
|
||||
:title="$t('metrics')"
|
||||
:execution="execution"
|
||||
:key="`metrics-${index}-${taskItem.id}`"
|
||||
:data="convertMetric(attempt.metrics)" />
|
||||
|
||||
:data="convertMetric(attempt.metrics)"
|
||||
/>
|
||||
</template>
|
||||
<!-- Outputs -->
|
||||
<vars
|
||||
@@ -83,13 +101,10 @@
|
||||
:title="$t('outputs')"
|
||||
:execution="execution"
|
||||
:key="taskItem.id"
|
||||
:data="taskItem.outputs" />
|
||||
|
||||
|
||||
:data="taskItem.outputs"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -104,36 +119,44 @@
|
||||
import ChartAreaspline from "vue-material-design-icons/ChartAreaspline";
|
||||
|
||||
export default {
|
||||
components: {LogLine, Restart, Clock, LocationExit, Vars, ChartAreaspline},
|
||||
components: {
|
||||
LogLine,
|
||||
Restart,
|
||||
Clock,
|
||||
LocationExit,
|
||||
Vars,
|
||||
ChartAreaspline,
|
||||
},
|
||||
props: {
|
||||
level: {
|
||||
type: String,
|
||||
default: "INFO"
|
||||
default: "INFO",
|
||||
},
|
||||
filter: {
|
||||
type: String,
|
||||
default: ""
|
||||
default: "",
|
||||
},
|
||||
taskRunId: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showOutputs: {},
|
||||
showMetrics: {}
|
||||
showMetrics: {},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
level: function () {
|
||||
this.loadLogs()
|
||||
}
|
||||
this.loadLogs();
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.loadLogs();
|
||||
},
|
||||
computed: {
|
||||
...mapState("execution", ["execution", "task", "logs"])
|
||||
...mapState("execution", ["execution", "task", "logs"]),
|
||||
},
|
||||
methods: {
|
||||
toggleShowOutput(task) {
|
||||
@@ -141,23 +164,25 @@
|
||||
this.$forceUpdate();
|
||||
},
|
||||
toggleShowMetric(task, index) {
|
||||
this.showMetrics[task.id + "-" + index] = !this.showMetrics[task.id + "-" + index];
|
||||
this.showMetrics[task.id + "-" + index] = !this.showMetrics[
|
||||
task.id + "-" + index
|
||||
];
|
||||
this.$forceUpdate();
|
||||
},
|
||||
loadLogs() {
|
||||
let params = {minLevel: this.level};
|
||||
|
||||
if (this.taskRunId) {
|
||||
params.taskRunId = this.taskRunId
|
||||
params.taskRunId = this.taskRunId;
|
||||
}
|
||||
|
||||
if (this.execution && this.execution.state.current === "RUNNING") {
|
||||
this.$store
|
||||
.dispatch("execution/followLogs", {
|
||||
id: this.$route.params.id,
|
||||
params: params
|
||||
params: params,
|
||||
})
|
||||
.then(sse => {
|
||||
.then((sse) => {
|
||||
this.sse = sse;
|
||||
this.$store.commit("execution/setLogs", []);
|
||||
|
||||
@@ -168,23 +193,26 @@
|
||||
} else {
|
||||
this.$store.dispatch("execution/loadLogs", {
|
||||
executionId: this.$route.params.id,
|
||||
params: params
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
},
|
||||
findLogs(taskRunId, attemptNumber) {
|
||||
return (this.logs || [])
|
||||
.filter(log => {
|
||||
return log.taskRunId === taskRunId && log.attemptNumber === attemptNumber;
|
||||
})
|
||||
return (this.logs || []).filter((log) => {
|
||||
return (
|
||||
log.taskRunId === taskRunId &&
|
||||
log.attemptNumber === attemptNumber
|
||||
);
|
||||
});
|
||||
},
|
||||
convertMetric(metrics) {
|
||||
return (metrics || [])
|
||||
.reduce((accumulator, r) => {
|
||||
accumulator[r.name] = r.type === "timer" ? humanizeDuration(parseInt(r.value * 1000)) : r.value
|
||||
return accumulator;
|
||||
}, Object.create(null));
|
||||
|
||||
return (metrics || []).reduce((accumulator, r) => {
|
||||
accumulator[r.name] =
|
||||
r.type === "timer"
|
||||
? humanizeDuration(parseInt(r.value * 1000))
|
||||
: r.value;
|
||||
return accumulator;
|
||||
}, Object.create(null));
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
@@ -192,7 +220,7 @@
|
||||
this.sse.close();
|
||||
this.sse = undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
<template>
|
||||
<div>
|
||||
<log-filters/>
|
||||
<log-filters />
|
||||
<log-list :level="level" :filter="filterTerm" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import LogList from "./LogList";
|
||||
import LogFilters from "./LogFilters";
|
||||
import { mapState } from "vuex";
|
||||
export default {
|
||||
components: { LogList, LogFilters },
|
||||
computed: {
|
||||
...mapState("execution", ["execution", "task", "logs"]),
|
||||
filterTerm() {
|
||||
return (this.$route.query.q || "").toLowerCase();
|
||||
import LogList from "./LogList";
|
||||
import LogFilters from "./LogFilters";
|
||||
import {mapState} from "vuex";
|
||||
export default {
|
||||
components: {LogList, LogFilters},
|
||||
computed: {
|
||||
...mapState("execution", ["execution", "task", "logs"]),
|
||||
filterTerm() {
|
||||
return (this.$route.query.q || "").toLowerCase();
|
||||
},
|
||||
level() {
|
||||
return this.$route.query.level || "INFO";
|
||||
},
|
||||
},
|
||||
level() {
|
||||
return this.$route.query.level || "INFO";
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -3,14 +3,16 @@
|
||||
<div class="log-content">
|
||||
<main-log-filter @onChange="loadData" />
|
||||
<div v-if="logs === undefined">
|
||||
<b-alert variant="light" show>{{$t('no result')}}</b-alert>
|
||||
<b-alert variant="light" show>
|
||||
{{ $t('no result') }}
|
||||
</b-alert>
|
||||
</div>
|
||||
<div class="bg-dark text-white">
|
||||
<template v-for="(log, i) in logs">
|
||||
<log-line
|
||||
level="TRACE"
|
||||
filter=""
|
||||
:excludeMetas="isFlowEdit ? ['namespace', 'flowId'] : []"
|
||||
:exclude-metas="isFlowEdit ? ['namespace', 'flowId'] : []"
|
||||
:log="log"
|
||||
:key="`${log.taskRunId}-${i}`"
|
||||
/>
|
||||
@@ -22,59 +24,59 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LogLine from "../logs/LogLine";
|
||||
import Pagination from "../layout/Pagination";
|
||||
import { mapState } from "vuex";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import MainLogFilter from "./MainLogFilter";
|
||||
import qb from "../../utils/queryBuilder";
|
||||
import LogLine from "../logs/LogLine";
|
||||
import Pagination from "../layout/Pagination";
|
||||
import {mapState} from "vuex";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import MainLogFilter from "./MainLogFilter";
|
||||
import qb from "../../utils/queryBuilder";
|
||||
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
components: { LogLine, Pagination, MainLogFilter },
|
||||
data() {
|
||||
return {
|
||||
task: undefined,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.loadData();
|
||||
},
|
||||
computed: {
|
||||
...mapState("log", ["logs", "total", "level"]),
|
||||
routeInfo() {
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
components: {LogLine, Pagination, MainLogFilter},
|
||||
data() {
|
||||
return {
|
||||
title: this.$t("logs"),
|
||||
task: undefined,
|
||||
};
|
||||
},
|
||||
isFlowEdit() {
|
||||
return this.$route.name === 'flowEdit'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onPageChanged(pagination) {
|
||||
this.$router.push({
|
||||
query: { ...this.$route.query, ...pagination },
|
||||
});
|
||||
created() {
|
||||
this.loadData();
|
||||
},
|
||||
loadData() {
|
||||
|
||||
let q = qb.logQueryBuilder(this.$route);
|
||||
if (this.isFlowEdit) {
|
||||
q += ` AND namespace:${this.$route.params.namespace}`
|
||||
q += ` AND flowId:${this.$route.params.id}`
|
||||
computed: {
|
||||
...mapState("log", ["logs", "total", "level"]),
|
||||
routeInfo() {
|
||||
return {
|
||||
title: this.$t("logs"),
|
||||
};
|
||||
},
|
||||
isFlowEdit() {
|
||||
return this.$route.name === "flowEdit"
|
||||
}
|
||||
|
||||
this.$store.dispatch("log/findLogs", {
|
||||
q,
|
||||
page: this.$route.query.page || 1,
|
||||
size: this.$route.query.size || 25,
|
||||
minLevel: this.$route.query.level || "INFO"
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
methods: {
|
||||
onPageChanged(pagination) {
|
||||
this.$router.push({
|
||||
query: {...this.$route.query, ...pagination},
|
||||
});
|
||||
this.loadData();
|
||||
},
|
||||
loadData() {
|
||||
|
||||
let q = qb.logQueryBuilder(this.$route);
|
||||
if (this.isFlowEdit) {
|
||||
q += ` AND namespace:${this.$route.params.namespace}`
|
||||
q += ` AND flowId:${this.$route.params.id}`
|
||||
}
|
||||
|
||||
this.$store.dispatch("log/findLogs", {
|
||||
q,
|
||||
page: this.$route.query.page || 1,
|
||||
size: this.$route.query.size || 25,
|
||||
minLevel: this.$route.query.level || "INFO"
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "../../styles/_variable.scss";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<b-navbar toggleable="lg" type="light" variant="light">
|
||||
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
|
||||
<b-navbar-toggle target="nav-collapse" />
|
||||
<b-collapse id="nav-collapse" is-nav>
|
||||
<b-nav-form>
|
||||
<search-field ref="searchField" @onSearch="onChange" />
|
||||
@@ -12,17 +12,17 @@
|
||||
</b-navbar>
|
||||
</template>
|
||||
<script>
|
||||
import NamespaceSelect from "../namespace/NamespaceSelect";
|
||||
import SearchField from "../layout/SearchField";
|
||||
import DateRange from "../layout/DateRange";
|
||||
import LogLevelSelector from "./LogLevelSelector";
|
||||
import NamespaceSelect from "../namespace/NamespaceSelect";
|
||||
import SearchField from "../layout/SearchField";
|
||||
import DateRange from "../layout/DateRange";
|
||||
import LogLevelSelector from "./LogLevelSelector";
|
||||
|
||||
export default {
|
||||
components: { NamespaceSelect, DateRange, SearchField, LogLevelSelector },
|
||||
methods: {
|
||||
onChange() {
|
||||
this.$emit("onChange");
|
||||
export default {
|
||||
components: {NamespaceSelect, DateRange, SearchField, LogLevelSelector},
|
||||
methods: {
|
||||
onChange() {
|
||||
this.$emit("onChange");
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -5,41 +5,41 @@
|
||||
:placeholder="$t('Select namespace')"
|
||||
:options="namespaces"
|
||||
class="ns-selector"
|
||||
></v-select>
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
export default {
|
||||
props: {
|
||||
dataType: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch("namespace/loadNamespaces", {dataType: this.dataType});
|
||||
this.selectedNamespace = this.$route.query.namespace || "";
|
||||
},
|
||||
computed: {
|
||||
...mapState("namespace", ["namespaces"])
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedNamespace: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onNamespaceSelect() {
|
||||
const query = { ...this.$route.query };
|
||||
query.namespace = this.selectedNamespace;
|
||||
if (!this.selectedNamespace) {
|
||||
delete query.namespace;
|
||||
import {mapState} from "vuex";
|
||||
export default {
|
||||
props: {
|
||||
dataType: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch("namespace/loadNamespaces", {dataType: this.dataType});
|
||||
this.selectedNamespace = this.$route.query.namespace || "";
|
||||
},
|
||||
computed: {
|
||||
...mapState("namespace", ["namespaces"])
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedNamespace: ""
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onNamespaceSelect() {
|
||||
const query = {...this.$route.query};
|
||||
query.namespace = this.selectedNamespace;
|
||||
if (!this.selectedNamespace) {
|
||||
delete query.namespace;
|
||||
}
|
||||
this.$router.push({query});
|
||||
this.$emit("onNamespaceSelect");
|
||||
}
|
||||
this.$router.push({ query });
|
||||
this.$emit("onNamespaceSelect");
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@
|
||||
<div>
|
||||
<b-row>
|
||||
<b-col md="9" class="markdown">
|
||||
<markdown v-if="plugin" :source="plugin.markdown" :permalink="true"></markdown>
|
||||
<markdown v-if="plugin" :source="plugin.markdown" :permalink="true" />
|
||||
<div v-else>
|
||||
<b-alert variant="info" show>{{ $t('plugins.please') }}</b-alert>
|
||||
<b-alert variant="info" show>
|
||||
{{ $t('plugins.please') }}
|
||||
</b-alert>
|
||||
</div>
|
||||
</b-col>
|
||||
<b-col md="3">
|
||||
<Toc @routerChange="routerChange" v-if="plugins" :plugins="plugins"></Toc>
|
||||
<Toc @routerChange="routerChange" v-if="plugins" :plugins="plugins" />
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
@@ -16,9 +18,9 @@
|
||||
|
||||
<script>
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import Markdown from '../layout/Markdown.vue'
|
||||
import Toc from './Toc.vue'
|
||||
import { mapState } from "vuex";
|
||||
import Markdown from "../layout/Markdown.vue"
|
||||
import Toc from "./Toc.vue"
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
@@ -65,7 +67,7 @@
|
||||
routerChange() {
|
||||
window.scroll({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
behavior: "smooth"
|
||||
})
|
||||
|
||||
this.loadPlugin();
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
<div class="plugins-list">
|
||||
<b-card class="accordion" no-body :key="plugin.manifest['X-Kestra-Title']" v-for="(plugin, index) in plugins">
|
||||
<b-card-header header-tag="header" class="p-0" role="tab">
|
||||
<b-button block v-b-toggle="plugin.manifest['X-Kestra-Title']" variant="light">{{ plugin.manifest['X-Kestra-Title'] }}</b-button>
|
||||
<b-button block v-b-toggle="plugin.manifest['X-Kestra-Title']" variant="light">
|
||||
{{ plugin.manifest['X-Kestra-Title'] }}
|
||||
</b-button>
|
||||
</b-card-header>
|
||||
<b-collapse :id="plugin.manifest['X-Kestra-Title']" :visible="index === 0" accordion="my-accordion" role="tabpanel">
|
||||
<b-collapse :id="plugin.manifest['X-Kestra-Title']" :visible="index === 0" accordion="my-accordion" role="tabpanel">
|
||||
<b-card-body>
|
||||
<ul class="section-nav toc-h3">
|
||||
<li v-for="(classes, namespace) in group(plugin.tasks)" :key="namespace">
|
||||
@@ -12,14 +14,13 @@
|
||||
<ul>
|
||||
<li v-for="cls in classes" :key="cls">
|
||||
<router-link
|
||||
v-on:click.native="$emit('routerChange')"
|
||||
:to="{ name: 'pluginView', params: {cls: namespace + '.' + cls}}"
|
||||
@click.native="$emit('routerChange')"
|
||||
:to="{name: 'pluginView', params: {cls: namespace + '.' + cls}}"
|
||||
>
|
||||
{{ cls }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
</b-card-body>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-form-group :label="$t('Language')" label-cols-sm="3">
|
||||
<b-form-select v-model="lang" :options="langOptions"></b-form-select>
|
||||
<b-form-select v-model="lang" :options="langOptions" />
|
||||
</b-form-group>
|
||||
<b-form-group :label="$t('Fold auto')" label-cols-sm="3">
|
||||
<b-checkbox v-model="autofoldTextEditor" value="1" unchecked-value="0" />
|
||||
@@ -10,45 +10,45 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
data() {
|
||||
return {
|
||||
langOptions: [
|
||||
{ value: "en", text: "English" },
|
||||
{ value: "fr", text: "Français" }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
routeInfo() {
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
export default {
|
||||
mixins: [RouteContext],
|
||||
data() {
|
||||
return {
|
||||
title: this.$t("settings")
|
||||
langOptions: [
|
||||
{value: "en", text: "English"},
|
||||
{value: "fr", text: "Français"}
|
||||
]
|
||||
};
|
||||
},
|
||||
lang: {
|
||||
set(lang) {
|
||||
localStorage.setItem("lang", lang);
|
||||
this.$moment.locale(lang);
|
||||
this.$root.$i18n.locale = lang;
|
||||
this.$toast().saved();
|
||||
computed: {
|
||||
routeInfo() {
|
||||
return {
|
||||
title: this.$t("settings")
|
||||
};
|
||||
},
|
||||
get() {
|
||||
return localStorage.getItem("lang") || "en";
|
||||
}
|
||||
},
|
||||
autofoldTextEditor: {
|
||||
set(value) {
|
||||
localStorage.setItem("autofoldTextEditor", value);
|
||||
|
||||
this.$toast().saved();
|
||||
lang: {
|
||||
set(lang) {
|
||||
localStorage.setItem("lang", lang);
|
||||
this.$moment.locale(lang);
|
||||
this.$root.$i18n.locale = lang;
|
||||
this.$toast().saved();
|
||||
},
|
||||
get() {
|
||||
return localStorage.getItem("lang") || "en";
|
||||
}
|
||||
},
|
||||
get() {
|
||||
return localStorage.getItem('autofoldTextEditor')
|
||||
}
|
||||
},
|
||||
autofoldTextEditor: {
|
||||
set(value) {
|
||||
localStorage.setItem("autofoldTextEditor", value);
|
||||
|
||||
}
|
||||
};
|
||||
this.$toast().saved();
|
||||
},
|
||||
get() {
|
||||
return localStorage.getItem("autofoldTextEditor")
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
<template>
|
||||
<div :id="uuid" :class="'executions-charts' + (this.global ? '' : ' mini')" v-if="dataReady">
|
||||
<current-chart :data="collections" :options="options"></current-chart>
|
||||
<current-chart :data="collections" :options="options" />
|
||||
<b-tooltip
|
||||
custom-class="tooltip-stats"
|
||||
no-fade
|
||||
:target="uuid"
|
||||
:placement="(this.global ? 'bottom' : 'left')"
|
||||
triggers="hover">
|
||||
<span v-html="tooltip"></span>
|
||||
triggers="hover"
|
||||
>
|
||||
<span v-html="tooltip" />
|
||||
</b-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Line} from 'vue-chartjs'
|
||||
import {Line} from "vue-chartjs"
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import {tooltip, defaultConfig} from "../../utils/charts.js";
|
||||
import Utils from "../../utils/utils";
|
||||
@@ -72,7 +73,7 @@
|
||||
datasets: [{
|
||||
label: "Duration",
|
||||
backgroundColor: "#c7e7e5",
|
||||
fill: 'start',
|
||||
fill: "start",
|
||||
pointRadius: 1,
|
||||
borderWidth: 1,
|
||||
borderColor: "#1dbaaf",
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
<template>
|
||||
<div :id="uuid" :class="'executions-charts' + (this.global ? '' : ' mini')" v-if="dataReady">
|
||||
<current-chart :data="collections" :options="options"></current-chart>
|
||||
<current-chart :data="collections" :options="options" />
|
||||
<b-tooltip
|
||||
custom-class="tooltip-stats"
|
||||
no-fade
|
||||
:target="uuid"
|
||||
:placement="(this.global ? 'bottom' : 'left')"
|
||||
triggers="hover">
|
||||
<span v-html="tooltip"></span>
|
||||
triggers="hover"
|
||||
>
|
||||
<span v-html="tooltip" />
|
||||
</b-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Bar} from 'vue-chartjs'
|
||||
import {Bar} from "vue-chartjs"
|
||||
import Utils from "../../utils/utils.js";
|
||||
import {tooltip, defaultConfig} from "../../utils/charts.js";
|
||||
import State from "../..//utils/state";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<div class="state-global-charts">
|
||||
<div class="title" :title="$t('last 30 days executions')">{{$t('last 30 days executions')}}</div>
|
||||
<div class="title" :title="$t('last 30 days executions')">
|
||||
{{ $t('last 30 days executions') }}
|
||||
</div>
|
||||
<state-chart
|
||||
v-if="ready"
|
||||
:data="data"
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
<div v-if="ready">
|
||||
<data-table @onPageChanged="onPageChanged" ref="dataTable" :total="total" :max="maxTaskRunSetting">
|
||||
<template v-slot:navbar>
|
||||
<search-field ref="searchField" @onSearch="onSearch" :fields="searchableFields"/>
|
||||
<search-field ref="searchField" @onSearch="onSearch" :fields="searchableFields" />
|
||||
<namespace-select
|
||||
data-type="flow"
|
||||
v-if="$route.name !== 'flowEdit'"
|
||||
@onNamespaceSelect="onNamespaceSelect"
|
||||
/>
|
||||
<status-filter-buttons @onRefresh="loadData"/>
|
||||
<date-range @onDate="onSearch"/>
|
||||
<refresh-button class="float-right" @onRefresh="loadData"/>
|
||||
<status-filter-buttons @onRefresh="loadData" />
|
||||
<date-range @onDate="onSearch" />
|
||||
<refresh-button class="float-right" @onRefresh="loadData" />
|
||||
</template>
|
||||
|
||||
<template v-slot:top>
|
||||
@@ -38,16 +38,18 @@
|
||||
</template>
|
||||
<template v-slot:cell(details)="row">
|
||||
<router-link
|
||||
:to="{name: 'executionEdit', params: {namespace: row.item.namespace, flowId: row.item.flowId, id: row.item.executionId},query: {tab:'gantt'} }">
|
||||
<eye id="edit-action"/>
|
||||
:to="{name: 'executionEdit', params: {namespace: row.item.namespace, flowId: row.item.flowId, id: row.item.executionId},query: {tab:'gantt'}}"
|
||||
>
|
||||
<eye id="edit-action" />
|
||||
</router-link>
|
||||
</template>
|
||||
<template v-slot:cell(state.startDate)="row">{{ row.item.state.startDate | date('LLLL') }}
|
||||
<template v-slot:cell(state.startDate)="row">
|
||||
{{ row.item.state.startDate | date('LLLL') }}
|
||||
</template>
|
||||
<template v-slot:cell(state.endDate)="row">
|
||||
<span
|
||||
v-if="!['RUNNING', 'CREATED'].includes(row.item.state.current)"
|
||||
>{{ row.item.state.endDate | date('LLLL') }}</span>
|
||||
<span
|
||||
v-if="!['RUNNING', 'CREATED'].includes(row.item.state.current)"
|
||||
>{{ row.item.state.endDate | date('LLLL') }}</span>
|
||||
</template>
|
||||
<template v-slot:cell(state.current)="row">
|
||||
<status
|
||||
@@ -57,15 +59,16 @@
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:cell(state.duration)="row">
|
||||
<span
|
||||
v-if="['RUNNING', 'CREATED'].includes(row.item.state.current)"
|
||||
>{{ durationFrom(row.item) | humanizeDuration }}</span>
|
||||
<span
|
||||
v-if="['RUNNING', 'CREATED'].includes(row.item.state.current)"
|
||||
>{{ durationFrom(row.item) | humanizeDuration }}</span>
|
||||
<span v-else>{{ row.item.state.duration | humanizeDuration }}</span>
|
||||
</template>
|
||||
<template v-slot:cell(flowId)="row">
|
||||
<router-link
|
||||
:to="{name: 'flowEdit', params: {namespace: row.item.namespace, id: row.item.flowId}}"
|
||||
>{{ row.item.flowId }}
|
||||
>
|
||||
{{ row.item.flowId }}
|
||||
</router-link>
|
||||
</template>
|
||||
<template v-slot:cell(id)="row">
|
||||
@@ -75,7 +78,7 @@
|
||||
<code>{{ row.item.executionId | id }}</code>
|
||||
</template>
|
||||
<template v-slot:cell(taskId)="row">
|
||||
<code v-b-tooltip.hover :title=row.item.taskId>{{ row.item.taskId | ellipsis(25) }} </code>
|
||||
<code v-b-tooltip.hover :title="row.item.taskId">{{ row.item.taskId | ellipsis(25) }} </code>
|
||||
</template>
|
||||
<template v-slot:cell(executionId)="row">
|
||||
<code>{{ row.item.executionId | id }}</code>
|
||||
@@ -214,15 +217,15 @@
|
||||
this.$router.push({
|
||||
name: "executionEdit",
|
||||
params: {namespace: item.namespace, flowId: item.flowId, id: item.executionId},
|
||||
query: {tab: 'gantt'}
|
||||
query: {tab: "gantt"}
|
||||
});
|
||||
},
|
||||
loadData(callback) {
|
||||
this.$store
|
||||
.dispatch("stat/taskRunDaily", {
|
||||
q: this.executionQuery,
|
||||
startDate: this.$moment(this.startDate).format('YYYY-MM-DD'),
|
||||
endDate: this.$moment(this.endDate).format('YYYY-MM-DD')
|
||||
startDate: this.$moment(this.startDate).format("YYYY-MM-DD"),
|
||||
endDate: this.$moment(this.endDate).format("YYYY-MM-DD")
|
||||
})
|
||||
.then(() => {
|
||||
this.dailyReady = true;
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<editor v-model="content" lang="yaml"></editor>
|
||||
<editor v-model="content" lang="yaml" />
|
||||
<bottom-line v-if="canSave || canDelete">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item">
|
||||
<b-button class="btn-danger" v-if="canDelete" @click="deleteFile">
|
||||
<delete />
|
||||
<span>{{$t('delete')}}</span>
|
||||
<span>{{ $t('delete') }}</span>
|
||||
</b-button>
|
||||
|
||||
<b-button @click="save" v-if="canSave">
|
||||
<content-save />
|
||||
<span>{{$t('save')}}</span>
|
||||
<span>{{ $t('save') }}</span>
|
||||
</b-button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -20,39 +20,39 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import flowTemplateEdit from "../../mixins/flowTemplateEdit";
|
||||
import { mapState } from "vuex";
|
||||
import flowTemplateEdit from "../../mixins/flowTemplateEdit";
|
||||
import {mapState} from "vuex";
|
||||
|
||||
export default {
|
||||
mixins: [flowTemplateEdit],
|
||||
data() {
|
||||
return {
|
||||
dataType: "template",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState("template", ["template"]),
|
||||
},
|
||||
watch: {
|
||||
'$route.params'() {
|
||||
export default {
|
||||
mixins: [flowTemplateEdit],
|
||||
data() {
|
||||
return {
|
||||
dataType: "template",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState("template", ["template"]),
|
||||
},
|
||||
watch: {
|
||||
"$route.params"() {
|
||||
this.reload()
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.reload()
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.reload()
|
||||
},
|
||||
destroyed() {
|
||||
this.$store.commit("template/setTemplate", undefined);
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
if (this.$route.name === "templateEdit") {
|
||||
this.$store
|
||||
.dispatch("template/loadTemplate", this.$route.params)
|
||||
.then(this.loadFile);
|
||||
destroyed() {
|
||||
this.$store.commit("template/setTemplate", undefined);
|
||||
},
|
||||
methods: {
|
||||
reload() {
|
||||
if (this.$route.name === "templateEdit") {
|
||||
this.$store
|
||||
.dispatch("template/loadTemplate", this.$route.params)
|
||||
.then(this.loadFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
<template v-slot:cell(id)="row">
|
||||
<router-link
|
||||
:to="{name: `${dataType}Edit`, params: {namespace: row.item.namespace, id: row.item.id}}"
|
||||
>{{row.item.id}}</router-link>
|
||||
>
|
||||
{{ row.item.id }}
|
||||
</router-link>
|
||||
</template>
|
||||
</b-table>
|
||||
</template>
|
||||
@@ -48,7 +50,7 @@
|
||||
<router-link :to="{name: 'templateAdd'}">
|
||||
<b-button variant="primary">
|
||||
<plus />
|
||||
{{$t('create')}}
|
||||
{{ $t('create') }}
|
||||
</b-button>
|
||||
</router-link>
|
||||
</li>
|
||||
@@ -58,75 +60,75 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from "vuex";
|
||||
import permission from "../../models/permission";
|
||||
import action from "../../models/action";
|
||||
import NamespaceSelect from "../namespace/NamespaceSelect";
|
||||
import Plus from "vue-material-design-icons/Plus";
|
||||
import Eye from "vue-material-design-icons/Eye";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import DataTableActions from "../../mixins/dataTableActions";
|
||||
import DataTable from "../layout/DataTable";
|
||||
import SearchField from "../layout/SearchField";
|
||||
import {mapState} from "vuex";
|
||||
import permission from "../../models/permission";
|
||||
import action from "../../models/action";
|
||||
import NamespaceSelect from "../namespace/NamespaceSelect";
|
||||
import Plus from "vue-material-design-icons/Plus";
|
||||
import Eye from "vue-material-design-icons/Eye";
|
||||
import BottomLine from "../layout/BottomLine";
|
||||
import RouteContext from "../../mixins/routeContext";
|
||||
import DataTableActions from "../../mixins/dataTableActions";
|
||||
import DataTable from "../layout/DataTable";
|
||||
import SearchField from "../layout/SearchField";
|
||||
|
||||
export default {
|
||||
mixins: [RouteContext, DataTableActions],
|
||||
components: {
|
||||
BottomLine,
|
||||
Plus,
|
||||
Eye,
|
||||
DataTable,
|
||||
SearchField,
|
||||
NamespaceSelect,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataType: "template",
|
||||
permission: permission,
|
||||
action: action,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState("template", ["templates", "total"]),
|
||||
...mapState("stat", ["dailyGroupByFlow", "daily"]),
|
||||
...mapState("auth", ["user"]),
|
||||
fields() {
|
||||
const title = (title) => {
|
||||
return this.$t(title);
|
||||
export default {
|
||||
mixins: [RouteContext, DataTableActions],
|
||||
components: {
|
||||
BottomLine,
|
||||
Plus,
|
||||
Eye,
|
||||
DataTable,
|
||||
SearchField,
|
||||
NamespaceSelect,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataType: "template",
|
||||
permission: permission,
|
||||
action: action,
|
||||
};
|
||||
return [
|
||||
{
|
||||
key: "id",
|
||||
label: title("template"),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
key: "namespace",
|
||||
label: title("namespace"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
label: "",
|
||||
class: "row-action",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
loadData(callback) {
|
||||
this.$store
|
||||
.dispatch("template/findTemplates", {
|
||||
q: this.query,
|
||||
size: parseInt(this.$route.query.size || 25),
|
||||
page: parseInt(this.$route.query.page || 1),
|
||||
sort: this.$route.query.sort,
|
||||
})
|
||||
.then(() => {
|
||||
callback();
|
||||
});
|
||||
computed: {
|
||||
...mapState("template", ["templates", "total"]),
|
||||
...mapState("stat", ["dailyGroupByFlow", "daily"]),
|
||||
...mapState("auth", ["user"]),
|
||||
fields() {
|
||||
const title = (title) => {
|
||||
return this.$t(title);
|
||||
};
|
||||
return [
|
||||
{
|
||||
key: "id",
|
||||
label: title("template"),
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
key: "namespace",
|
||||
label: title("namespace"),
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
key: "actions",
|
||||
label: "",
|
||||
class: "row-action",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
methods: {
|
||||
loadData(callback) {
|
||||
this.$store
|
||||
.dispatch("template/findTemplates", {
|
||||
q: this.query,
|
||||
size: parseInt(this.$route.query.size || 25),
|
||||
page: parseInt(this.$route.query.page || 1),
|
||||
sort: this.$route.query.sort,
|
||||
})
|
||||
.then(() => {
|
||||
callback();
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
import humanizeDuration from "humanize-duration";
|
||||
|
||||
Vue.filter('id', value => value ? value.toString().substr(0, 8) : '');
|
||||
Vue.filter("id", value => value ? value.toString().substr(0, 8) : "");
|
||||
|
||||
Vue.filter('humanizeDuration', (value, options) => {
|
||||
options = options || { maxDecimalPoints: 2 }
|
||||
options.spacer = ''
|
||||
var language = localStorage.getItem('lang') || 'en'
|
||||
Vue.filter("humanizeDuration", (value, options) => {
|
||||
options = options || {maxDecimalPoints: 2}
|
||||
options.spacer = ""
|
||||
var language = localStorage.getItem("lang") || "en"
|
||||
options.language = language
|
||||
options.languages = {}
|
||||
options.languages[language] = {
|
||||
y: () => 'y',
|
||||
mo: () => 'mo',
|
||||
w: () => 'w',
|
||||
d: () => 'd',
|
||||
h: () => 'h',
|
||||
m: () => 'm',
|
||||
s: () => 's',
|
||||
ms: () => 'ms',
|
||||
y: () => "y",
|
||||
mo: () => "mo",
|
||||
w: () => "w",
|
||||
d: () => "d",
|
||||
h: () => "h",
|
||||
m: () => "m",
|
||||
s: () => "s",
|
||||
ms: () => "ms",
|
||||
}
|
||||
return humanizeDuration(value * 1000, options)
|
||||
});
|
||||
|
||||
Vue.filter('cap', value => value ? value.toString().capitalize() : '');
|
||||
Vue.filter("cap", value => value ? value.toString().capitalize() : "");
|
||||
|
||||
Vue.filter('lower', value => value ? value.toString().toLowerCase() : '');
|
||||
Vue.filter("lower", value => value ? value.toString().toLowerCase() : "");
|
||||
|
||||
Vue.filter('date', (dateString, format) => {
|
||||
Vue.filter("date", (dateString, format) => {
|
||||
let f;
|
||||
if (format === 'full') {
|
||||
f = 'MMMM Do YYYY, h: mm: ss'
|
||||
} else if (format === 'human') {
|
||||
f = 'LLLL'
|
||||
if (format === "full") {
|
||||
f = "MMMM Do YYYY, h: mm: ss"
|
||||
} else if (format === "human") {
|
||||
f = "LLLL"
|
||||
} else {
|
||||
f = format
|
||||
}
|
||||
return Vue.moment(dateString).format(f)
|
||||
})
|
||||
Vue.filter('ellipsis', (text, len) => text.length > len ? text.substr(0, len) + '...' : text.substr(0, len))
|
||||
Vue.filter("ellipsis", (text, len) => text.length > len ? text.substr(0, len) + "..." : text.substr(0, len))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Vue from 'vue';
|
||||
import VueAxios from 'vue-axios';
|
||||
import axios from 'axios';
|
||||
import Vue from "vue";
|
||||
import VueAxios from "vue-axios";
|
||||
import axios from "axios";
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
let root = (process.env.VUE_APP_API_URL || "") + KESTRA_BASE_PATH;
|
||||
@@ -12,7 +12,7 @@ export default (callback, store, nprogress) => {
|
||||
const instance = axios.create({
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
onUploadProgress: function (progressEvent) {
|
||||
if (progressEvent && progressEvent.loaded && progressEvent.total) {
|
||||
@@ -27,7 +27,7 @@ export default (callback, store, nprogress) => {
|
||||
return response
|
||||
}, errorResponse => {
|
||||
if (errorResponse.response && errorResponse.response.data) {
|
||||
store.dispatch('core/showErrorMessage', errorResponse.response.data)
|
||||
store.dispatch("core/showErrorMessage", errorResponse.response.data)
|
||||
}
|
||||
|
||||
return Promise.reject(errorResponse);
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
import './filters'
|
||||
import 'moment/locale/fr'
|
||||
import './utils/global'
|
||||
import './custom.scss'
|
||||
import "./filters"
|
||||
import "moment/locale/fr"
|
||||
import "./utils/global"
|
||||
import "./custom.scss"
|
||||
// @TODO: move to scss
|
||||
import 'vue-material-design-icons/styles.css';
|
||||
import "vue-material-design-icons/styles.css";
|
||||
|
||||
import App from './App.vue'
|
||||
import BootstrapVue from 'bootstrap-vue'
|
||||
import Vue from 'vue'
|
||||
import VueI18n from 'vue-i18n'
|
||||
import VueMoment from 'vue-moment'
|
||||
import NProgress from 'vue-nprogress'
|
||||
import App from "./App.vue"
|
||||
import BootstrapVue from "bootstrap-vue"
|
||||
import Vue from "vue"
|
||||
import VueI18n from "vue-i18n"
|
||||
import VueMoment from "vue-moment"
|
||||
import NProgress from "vue-nprogress"
|
||||
|
||||
import VueRouter from 'vue-router'
|
||||
import VueSSE from 'vue-sse';
|
||||
import VueSidebarMenu from 'vue-sidebar-menu'
|
||||
import VueRouter from "vue-router"
|
||||
import VueSSE from "vue-sse";
|
||||
import VueSidebarMenu from "vue-sidebar-menu"
|
||||
import Vuex from "vuex";
|
||||
import VueAnalytics from 'vue-analytics';
|
||||
import VueAnalytics from "vue-analytics";
|
||||
|
||||
import configureHttp from './http'
|
||||
import configureHttp from "./http"
|
||||
import Toast from "./utils/toast";
|
||||
import { extendMoment } from 'moment-range';
|
||||
import Translations from './translations.json'
|
||||
import moment from 'moment'
|
||||
import routes from './routes/routes'
|
||||
import stores from './stores/store'
|
||||
import vSelect from 'vue-select'
|
||||
import VueHotkey from 'v-hotkey'
|
||||
import {extendMoment} from "moment-range";
|
||||
import Translations from "./translations.json"
|
||||
import moment from "moment"
|
||||
import routes from "./routes/routes"
|
||||
import stores from "./stores/store"
|
||||
import vSelect from "vue-select"
|
||||
import VueHotkey from "v-hotkey"
|
||||
|
||||
let app = document.querySelector('#app');
|
||||
let app = document.querySelector("#app");
|
||||
|
||||
if (app) {
|
||||
Vue.use(Vuex)
|
||||
@@ -49,7 +49,7 @@ if (app) {
|
||||
Vue.use(VueI18n);
|
||||
|
||||
let i18n = new VueI18n({
|
||||
locale: localStorage.getItem('lang') || 'en',
|
||||
locale: localStorage.getItem("lang") || "en",
|
||||
messages: Translations
|
||||
});
|
||||
|
||||
@@ -60,13 +60,13 @@ if (app) {
|
||||
|
||||
Vue.use(VueHotkey)
|
||||
Vue.use(VueSSE);
|
||||
Vue.use(VueMoment, { moment: extendMoment(moment) });
|
||||
Vue.use(VueMoment, {moment: extendMoment(moment)});
|
||||
Vue.use(VueSidebarMenu);
|
||||
Vue.use(BootstrapVue);
|
||||
|
||||
Vue.use(Toast)
|
||||
|
||||
Vue.component('v-select', vSelect);
|
||||
Vue.component("v-select", vSelect);
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export default {
|
||||
computed: {
|
||||
routeInfo() {
|
||||
return {
|
||||
title: this.$t(this.dataType + 's')
|
||||
title: this.$t(this.dataType + "s")
|
||||
};
|
||||
},
|
||||
storageName() {
|
||||
@@ -25,7 +25,7 @@ export default {
|
||||
return this.fields.filter(f => f.sortable);
|
||||
},
|
||||
isBasePage() {
|
||||
return ['executionsList', 'flowsList'].includes(this.$route.name)
|
||||
return ["executionsList", "flowsList"].includes(this.$route.name)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -39,13 +39,13 @@ export default {
|
||||
`${sortItem.sortBy}:${sortItem.sortDesc ? "desc" : "asc"}`
|
||||
];
|
||||
this.$router.push({
|
||||
query: { ...this.$route.query, sort }
|
||||
query: {...this.$route.query, sort}
|
||||
});
|
||||
this.loadData(this.onDataLoaded);
|
||||
this.saveFilters()
|
||||
},
|
||||
onRowDoubleClick(item) {
|
||||
this.$router.push({ name: this.dataType + "Edit", params: item });
|
||||
this.$router.push({name: this.dataType + "Edit", params: item});
|
||||
},
|
||||
onPageChanged(item) {
|
||||
this.$router.push({
|
||||
@@ -76,10 +76,10 @@ export default {
|
||||
}
|
||||
},
|
||||
loadFilters () {
|
||||
const query = { ...this.$route.query}
|
||||
const query = {...this.$route.query}
|
||||
let change = false
|
||||
if (this.isBasePage) {
|
||||
const userPreferences = JSON.parse(localStorage.getItem(this.storageName) || '{}')
|
||||
const userPreferences = JSON.parse(localStorage.getItem(this.storageName) || "{}")
|
||||
for (const key in userPreferences) {
|
||||
if (!query[key] && userPreferences[key]) {
|
||||
query[key] = userPreferences[key]
|
||||
@@ -88,7 +88,7 @@ export default {
|
||||
}
|
||||
}
|
||||
if (change) {
|
||||
this.$router.push({ query: query });
|
||||
this.$router.push({query: query});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { canSaveFlowTemplate, saveFlowTemplate } from "../utils/flowTemplate";
|
||||
import { mapGetters, mapState } from "vuex";
|
||||
import {canSaveFlowTemplate, saveFlowTemplate} from "../utils/flowTemplate";
|
||||
import {mapGetters, mapState} from "vuex";
|
||||
|
||||
import BottomLine from "../components/layout/BottomLine";
|
||||
import ContentSave from "vue-material-design-icons/ContentSave";
|
||||
@@ -124,12 +124,12 @@ export default {
|
||||
} else {
|
||||
const item = YamlUtils.parse(this.content);
|
||||
this.$store
|
||||
.dispatch(`${this.dataType}/create${this.dataType.capitalize()}`, { [this.dataType]: item})
|
||||
.dispatch(`${this.dataType}/create${this.dataType.capitalize()}`, {[this.dataType]: item})
|
||||
.then(() => {
|
||||
this.$router.push({
|
||||
name: `${this.dataType}Edit`,
|
||||
params: item,
|
||||
query: { tab: "data-source" }
|
||||
query: {tab: "data-source"}
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
@@ -16,7 +16,7 @@ export default {
|
||||
methods: {
|
||||
onLoad() {
|
||||
if (!this.preventRouteInfo) {
|
||||
this.$store.commit('layout/setTopNavbar', this.routeInfo)
|
||||
this.$store.commit("layout/setTopNavbar", this.routeInfo)
|
||||
|
||||
let baseTitle;
|
||||
|
||||
|
||||
@@ -1,145 +1,145 @@
|
||||
<template>
|
||||
<sidebar-menu
|
||||
:menu="menu"
|
||||
@toggle-collapse="onToggleCollapse"
|
||||
width="200px"
|
||||
:collapsed="collapsed"
|
||||
>
|
||||
<div class="logo" slot="header">
|
||||
<router-link :to="{name: 'home'}">
|
||||
<span>
|
||||
<span class="img" />
|
||||
<sidebar-menu
|
||||
:menu="menu"
|
||||
@toggle-collapse="onToggleCollapse"
|
||||
width="200px"
|
||||
:collapsed="collapsed"
|
||||
>
|
||||
<div class="logo" slot="header">
|
||||
<router-link :to="{name: 'home'}">
|
||||
<span>
|
||||
<span class="img" />
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<span slot="toggle-icon">
|
||||
<chevron-right v-if="collapsed" />
|
||||
<chevron-left v-else />
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<span slot="toggle-icon">
|
||||
<chevron-right v-if="collapsed" />
|
||||
<chevron-left v-else />
|
||||
</span>
|
||||
</sidebar-menu>
|
||||
</sidebar-menu>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from "vue";
|
||||
import { SidebarMenu } from "vue-sidebar-menu";
|
||||
import ChevronLeft from "vue-material-design-icons/ChevronLeft";
|
||||
import ChevronRight from "vue-material-design-icons/ChevronRight";
|
||||
import Graph from "vue-material-design-icons/Graph";
|
||||
import Cog from "vue-material-design-icons/Cog";
|
||||
import TimelineClock from "vue-material-design-icons/TimelineClock";
|
||||
import BookOpen from "vue-material-design-icons/BookOpen";
|
||||
import CardText from "vue-material-design-icons/CardText";
|
||||
import HexagonMultiple from "vue-material-design-icons/HexagonMultiple";
|
||||
import ChartTimeline from "vue-material-design-icons/ChartTimeline";
|
||||
import Vue from "vue";
|
||||
import {SidebarMenu} from "vue-sidebar-menu";
|
||||
import ChevronLeft from "vue-material-design-icons/ChevronLeft";
|
||||
import ChevronRight from "vue-material-design-icons/ChevronRight";
|
||||
import Graph from "vue-material-design-icons/Graph";
|
||||
import Cog from "vue-material-design-icons/Cog";
|
||||
import TimelineClock from "vue-material-design-icons/TimelineClock";
|
||||
import BookOpen from "vue-material-design-icons/BookOpen";
|
||||
import CardText from "vue-material-design-icons/CardText";
|
||||
import HexagonMultiple from "vue-material-design-icons/HexagonMultiple";
|
||||
import ChartTimeline from "vue-material-design-icons/ChartTimeline";
|
||||
|
||||
Vue.component("graph", Graph);
|
||||
Vue.component("settings", Cog);
|
||||
Vue.component("timelineclock", TimelineClock);
|
||||
Vue.component("bookopen", BookOpen);
|
||||
Vue.component("cardtext", CardText);
|
||||
Vue.component("hexagon-multiple", HexagonMultiple);
|
||||
Vue.component("charttimeline", ChartTimeline);
|
||||
Vue.component("graph", Graph);
|
||||
Vue.component("settings", Cog);
|
||||
Vue.component("timelineclock", TimelineClock);
|
||||
Vue.component("bookopen", BookOpen);
|
||||
Vue.component("cardtext", CardText);
|
||||
Vue.component("hexagon-multiple", HexagonMultiple);
|
||||
Vue.component("charttimeline", ChartTimeline);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
SidebarMenu,
|
||||
},
|
||||
methods: {
|
||||
onToggleCollapse(folded) {
|
||||
this.collapsed = folded;
|
||||
localStorage.setItem("menuCollapsed", folded ? "true" : "false");
|
||||
this.$emit("onMenuCollapse", folded);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
collapsed: localStorage.getItem("menuCollapsed") === "true"
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
menu() {
|
||||
return [
|
||||
{
|
||||
href: "/flows",
|
||||
alias: [
|
||||
"/flows*"
|
||||
],
|
||||
title: this.$t("flows"),
|
||||
icon: {
|
||||
element: "graph",
|
||||
class: "menu-icon"
|
||||
}
|
||||
export default {
|
||||
components: {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
SidebarMenu,
|
||||
},
|
||||
{
|
||||
href: "/templates",
|
||||
alias: [
|
||||
"/templates*"
|
||||
],
|
||||
title: this.$t("templates"),
|
||||
icon: {
|
||||
element: "cardtext",
|
||||
class: "menu-icon"
|
||||
methods: {
|
||||
onToggleCollapse(folded) {
|
||||
this.collapsed = folded;
|
||||
localStorage.setItem("menuCollapsed", folded ? "true" : "false");
|
||||
this.$emit("onMenuCollapse", folded);
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/executions",
|
||||
alias: [
|
||||
"/executions*"
|
||||
],
|
||||
title: this.$t("executions"),
|
||||
icon: {
|
||||
element: "timelineclock",
|
||||
class: "menu-icon"
|
||||
}
|
||||
data() {
|
||||
return {
|
||||
collapsed: localStorage.getItem("menuCollapsed") === "true"
|
||||
};
|
||||
},
|
||||
{
|
||||
href: "/taskruns",
|
||||
alias: ["/taskruns*"],
|
||||
title: this.$t("taskruns"),
|
||||
icon: {
|
||||
element: "charttimeline",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/logs",
|
||||
alias: [
|
||||
"/logs*"
|
||||
],
|
||||
title: this.$t("logs"),
|
||||
icon: {
|
||||
element: "hexagon-multiple",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/plugins",
|
||||
alias: [
|
||||
"/plugins*"
|
||||
],
|
||||
title: this.$t("plugins.documentation"),
|
||||
icon: {
|
||||
element: "bookopen",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/settings",
|
||||
alias: [
|
||||
"/settings*"
|
||||
],
|
||||
title: this.$t("settings"),
|
||||
icon: {
|
||||
element: "settings",
|
||||
class: "menu-icon"
|
||||
}
|
||||
computed: {
|
||||
menu() {
|
||||
return [
|
||||
{
|
||||
href: "/flows",
|
||||
alias: [
|
||||
"/flows*"
|
||||
],
|
||||
title: this.$t("flows"),
|
||||
icon: {
|
||||
element: "graph",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/templates",
|
||||
alias: [
|
||||
"/templates*"
|
||||
],
|
||||
title: this.$t("templates"),
|
||||
icon: {
|
||||
element: "cardtext",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/executions",
|
||||
alias: [
|
||||
"/executions*"
|
||||
],
|
||||
title: this.$t("executions"),
|
||||
icon: {
|
||||
element: "timelineclock",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/taskruns",
|
||||
alias: ["/taskruns*"],
|
||||
title: this.$t("taskruns"),
|
||||
icon: {
|
||||
element: "charttimeline",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/logs",
|
||||
alias: [
|
||||
"/logs*"
|
||||
],
|
||||
title: this.$t("logs"),
|
||||
icon: {
|
||||
element: "hexagon-multiple",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/plugins",
|
||||
alias: [
|
||||
"/plugins*"
|
||||
],
|
||||
title: this.$t("plugins.documentation"),
|
||||
icon: {
|
||||
element: "bookopen",
|
||||
class: "menu-icon"
|
||||
}
|
||||
},
|
||||
{
|
||||
href: "/settings",
|
||||
alias: [
|
||||
"/settings*"
|
||||
],
|
||||
title: this.$t("settings"),
|
||||
icon: {
|
||||
element: "settings",
|
||||
class: "menu-icon"
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div/>
|
||||
<div />
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
|
||||
@@ -1,46 +1,46 @@
|
||||
import ExecutionRoot from '../components/executions/ExecutionRoot.vue'
|
||||
import Executions from '../components/executions/Executions.vue'
|
||||
import TaskRuns from '../components/taskruns/TaskRuns.vue'
|
||||
import FlowEdit from '../components/flows/FlowEdit.vue'
|
||||
import FlowRoot from '../components/flows/FlowRoot.vue'
|
||||
import Flows from '../components/flows/Flows.vue'
|
||||
import LogsWrapper from '../components/logs/LogsWrapper.vue'
|
||||
import Plugin from '../components/plugins/Plugin.vue'
|
||||
import Settings from '../components/settings/Settings.vue'
|
||||
import TemplateEdit from '../components/templates/TemplateEdit.vue'
|
||||
import Templates from '../components/templates/Templates.vue'
|
||||
import ExecutionRoot from "../components/executions/ExecutionRoot.vue"
|
||||
import Executions from "../components/executions/Executions.vue"
|
||||
import TaskRuns from "../components/taskruns/TaskRuns.vue"
|
||||
import FlowEdit from "../components/flows/FlowEdit.vue"
|
||||
import FlowRoot from "../components/flows/FlowRoot.vue"
|
||||
import Flows from "../components/flows/Flows.vue"
|
||||
import LogsWrapper from "../components/logs/LogsWrapper.vue"
|
||||
import Plugin from "../components/plugins/Plugin.vue"
|
||||
import Settings from "../components/settings/Settings.vue"
|
||||
import TemplateEdit from "../components/templates/TemplateEdit.vue"
|
||||
import Templates from "../components/templates/Templates.vue"
|
||||
|
||||
export default {
|
||||
mode: 'history',
|
||||
mode: "history",
|
||||
// eslint-disable-next-line no-undef
|
||||
base: KESTRA_UI_PATH,
|
||||
routes: [
|
||||
//Flows
|
||||
{ name: 'home', path: '/', component: Flows },
|
||||
{ name: 'flowsList', path: '/flows', component: Flows },
|
||||
{ name: 'flowsAdd', path: '/flows/new', component: FlowEdit },
|
||||
{ name: 'flowEdit', path: '/flows/edit/:namespace/:id', component: FlowRoot },
|
||||
{name: "home", path: "/", component: Flows},
|
||||
{name: "flowsList", path: "/flows", component: Flows},
|
||||
{name: "flowsAdd", path: "/flows/new", component: FlowEdit},
|
||||
{name: "flowEdit", path: "/flows/edit/:namespace/:id", component: FlowRoot},
|
||||
|
||||
//Executions
|
||||
{ name: 'executionsList', path: '/executions', component: Executions },
|
||||
{ name: 'executionEdit', path: '/executions/:namespace/:flowId/:id', component: ExecutionRoot },
|
||||
{name: "executionsList", path: "/executions", component: Executions},
|
||||
{name: "executionEdit", path: "/executions/:namespace/:flowId/:id", component: ExecutionRoot},
|
||||
|
||||
//TaskRuns
|
||||
{ name: 'taskRunList', path: '/taskruns', component: TaskRuns },
|
||||
{name: "taskRunList", path: "/taskruns", component: TaskRuns},
|
||||
|
||||
//Documentation
|
||||
{ name: 'plugin', path: '/plugins', component: Plugin },
|
||||
{ name: 'pluginView', path: '/plugins/:cls', component: Plugin },
|
||||
{name: "plugin", path: "/plugins", component: Plugin},
|
||||
{name: "pluginView", path: "/plugins/:cls", component: Plugin},
|
||||
|
||||
//Templates
|
||||
{ name: 'templateList', path: '/templates', component: Templates },
|
||||
{ name: 'templateAdd', path: '/templates/new', component: TemplateEdit },
|
||||
{ name: 'templateEdit', path: '/templates/edit/:namespace/:id', component: TemplateEdit },
|
||||
{name: "templateList", path: "/templates", component: Templates},
|
||||
{name: "templateAdd", path: "/templates/new", component: TemplateEdit},
|
||||
{name: "templateEdit", path: "/templates/edit/:namespace/:id", component: TemplateEdit},
|
||||
|
||||
//Settings
|
||||
{ name: 'logs', path: '/logs', component: LogsWrapper },
|
||||
{name: "logs", path: "/logs", component: LogsWrapper},
|
||||
|
||||
//Settings
|
||||
{ name: 'settings', path: '/settings', component: Settings },
|
||||
{name: "settings", path: "/settings", component: Settings},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export default {
|
||||
},
|
||||
actions: {
|
||||
showErrorMessage({commit}, message) {
|
||||
commit('setErrorMessage', message)
|
||||
commit("setErrorMessage", message)
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
@@ -10,68 +10,68 @@ export default {
|
||||
logs: []
|
||||
},
|
||||
actions: {
|
||||
loadExecutions({ commit }, options) {
|
||||
return Vue.axios.get(`/api/v1/executions`, { params: options }).then(response => {
|
||||
commit('setExecutions', response.data.results)
|
||||
commit('setTotal', response.data.total)
|
||||
loadExecutions({commit}, options) {
|
||||
return Vue.axios.get("/api/v1/executions", {params: options}).then(response => {
|
||||
commit("setExecutions", response.data.results)
|
||||
commit("setTotal", response.data.total)
|
||||
})
|
||||
},
|
||||
restartExecution(_, options) {
|
||||
return Vue.axios.post(`/api/v1/executions/${options.id}/restart?taskId=${options.taskId}`, { params: options }, {
|
||||
return Vue.axios.post(`/api/v1/executions/${options.id}/restart?taskId=${options.taskId}`, {params: options}, {
|
||||
headers: {
|
||||
'content-type': 'multipart/form-data'
|
||||
"content-type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
},
|
||||
kill(_, options) {
|
||||
return Vue.axios.delete(`/api/v1/executions/${options.id}/kill`);
|
||||
},
|
||||
loadExecution({ commit }, options) {
|
||||
loadExecution({commit}, options) {
|
||||
return Vue.axios.get(`/api/v1/executions/${options.id}`).then(response => {
|
||||
commit('setExecution', response.data)
|
||||
commit("setExecution", response.data)
|
||||
})
|
||||
},
|
||||
findExecutions({ commit }, options) {
|
||||
findExecutions({commit}, options) {
|
||||
const sort = options.sort
|
||||
delete options.sort
|
||||
let sortQueryString = ''
|
||||
let sortQueryString = ""
|
||||
if (sort) {
|
||||
sortQueryString = `?sort=${sort}`
|
||||
}
|
||||
return Vue.axios.get(`/api/v1/executions/search${sortQueryString}`, { params: options }).then(response => {
|
||||
commit('setExecutions', response.data.results)
|
||||
commit('setTotal', response.data.total)
|
||||
return Vue.axios.get(`/api/v1/executions/search${sortQueryString}`, {params: options}).then(response => {
|
||||
commit("setExecutions", response.data.results)
|
||||
commit("setTotal", response.data.total)
|
||||
})
|
||||
},
|
||||
triggerExecution(_, options) {
|
||||
return Vue.axios.post(`/api/v1/executions/trigger/${options.namespace}/${options.id}`, options.formData, {
|
||||
timeout: 60 * 60 * 1000,
|
||||
headers: {
|
||||
'content-type': 'multipart/form-data'
|
||||
"content-type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
},
|
||||
createFlow({ commit }, options) {
|
||||
return Vue.axios.post('/api/v1/executions', options.execution).then(response => {
|
||||
commit('setFlow', response.data.flow)
|
||||
createFlow({commit}, options) {
|
||||
return Vue.axios.post("/api/v1/executions", options.execution).then(response => {
|
||||
commit("setFlow", response.data.flow)
|
||||
})
|
||||
},
|
||||
followExecution(_, options) {
|
||||
return Vue.SSE(`${Vue.axios.defaults.baseURL}api/v1/executions/${options.id}/follow`, { format: 'json' })
|
||||
return Vue.SSE(`${Vue.axios.defaults.baseURL}api/v1/executions/${options.id}/follow`, {format: "json"})
|
||||
},
|
||||
followLogs(_, options) {
|
||||
return Vue.SSE(`${Vue.axios.defaults.baseURL}api/v1/logs/${options.id}/follow`, { format: 'json', params: options.params })
|
||||
return Vue.SSE(`${Vue.axios.defaults.baseURL}api/v1/logs/${options.id}/follow`, {format: "json", params: options.params})
|
||||
},
|
||||
loadTree({ commit }, execution) {
|
||||
loadTree({commit}, execution) {
|
||||
return Vue.axios.get(`/api/v1/executions/${execution.id}/tree`).then(response => {
|
||||
commit('setDataTree', response.data.tasks)
|
||||
commit("setDataTree", response.data.tasks)
|
||||
})
|
||||
},
|
||||
loadLogs({ commit }, options) {
|
||||
loadLogs({commit}, options) {
|
||||
return Vue.axios.get(`/api/v1/logs/${options.executionId}`, {
|
||||
params: options.params
|
||||
}).then(response => {
|
||||
commit('setLogs', response.data)
|
||||
commit("setLogs", response.data)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
@@ -10,58 +10,58 @@ export default {
|
||||
},
|
||||
|
||||
actions: {
|
||||
findFlows({ commit }, options) {
|
||||
const sortString = options.sort ? `?sort=${options.sort}` : ''
|
||||
findFlows({commit}, options) {
|
||||
const sortString = options.sort ? `?sort=${options.sort}` : ""
|
||||
delete options.sort
|
||||
return Vue.axios.get(`/api/v1/flows/search${sortString}`, {
|
||||
params: options
|
||||
}).then(response => {
|
||||
commit('setFlows', response.data.results)
|
||||
commit('setTotal', response.data.total)
|
||||
commit("setFlows", response.data.results)
|
||||
commit("setTotal", response.data.total)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
},
|
||||
loadFlow({ commit }, options) {
|
||||
loadFlow({commit}, options) {
|
||||
return Vue.axios.get(`/api/v1/flows/${options.namespace}/${options.id}`).then(response => {
|
||||
commit('setFlow', response.data)
|
||||
commit("setFlow", response.data)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
},
|
||||
saveFlow({ commit }, options) {
|
||||
saveFlow({commit}, options) {
|
||||
return Vue.axios.put(`/api/v1/flows/${options.flow.namespace}/${options.flow.id}`, options.flow).then(response => {
|
||||
if (response.status >= 300) {
|
||||
return Promise.reject(new Error("Server error on flow save"))
|
||||
} else {
|
||||
commit('setFlow', response.data)
|
||||
commit("setFlow", response.data)
|
||||
|
||||
return response.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
createFlow({ commit }, options) {
|
||||
return Vue.axios.post('/api/v1/flows', options.flow).then(response => {
|
||||
commit('setFlow', response.data)
|
||||
createFlow({commit}, options) {
|
||||
return Vue.axios.post("/api/v1/flows", options.flow).then(response => {
|
||||
commit("setFlow", response.data)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
},
|
||||
deleteFlow({ commit }, flow) {
|
||||
deleteFlow({commit}, flow) {
|
||||
return Vue.axios.delete(`/api/v1/flows/${flow.namespace}/${flow.id}`).then(() => {
|
||||
commit('setFlow', null)
|
||||
commit("setFlow", null)
|
||||
})
|
||||
},
|
||||
loadTree({ commit }, flow) {
|
||||
loadTree({commit}, flow) {
|
||||
return Vue.axios.get(`/api/v1/flows/${flow.namespace}/${flow.id}/tree`).then(response => {
|
||||
commit('setDataTree', response.data.tasks)
|
||||
commit("setDataTree", response.data.tasks)
|
||||
|
||||
return response.data.tasks;
|
||||
})
|
||||
},
|
||||
loadRevisions({ commit }, options) {
|
||||
loadRevisions({commit}, options) {
|
||||
return Vue.axios.get(`/api/v1/flows/${options.namespace}/${options.id}/revisions`).then(response => {
|
||||
commit('setRevisions', response.data)
|
||||
commit("setRevisions", response.data)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
|
||||
@@ -5,17 +5,17 @@ export default {
|
||||
configurationPanelPosition: undefined,
|
||||
},
|
||||
actions: {
|
||||
updateConfigurationPanelPosition({ commit, state }) {
|
||||
updateConfigurationPanelPosition({commit, state}) {
|
||||
if (state.node) {
|
||||
const position = {
|
||||
right: state.node.getCTM().e + 95,
|
||||
top: state.node.getCTM().f + 10
|
||||
}
|
||||
commit('setConfigurationPanelPosition', position)
|
||||
commit("setConfigurationPanelPosition", position)
|
||||
}
|
||||
},
|
||||
setNode({commit}, node) {
|
||||
commit('setNode', node)
|
||||
commit("setNode", node)
|
||||
// dispatch('updateConfigurationPanelPosition')
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
logs: undefined,
|
||||
total: 0,
|
||||
level: 'INFO'
|
||||
level: "INFO"
|
||||
},
|
||||
actions: {
|
||||
findLogs({ commit }, options) {
|
||||
return Vue.axios.get(`/api/v1/logs/search`, { params: options }).then(response => {
|
||||
commit('setLogs', response.data.results)
|
||||
commit('setTotal', response.data.total)
|
||||
findLogs({commit}, options) {
|
||||
return Vue.axios.get("/api/v1/logs/search", {params: options}).then(response => {
|
||||
commit("setLogs", response.data.results)
|
||||
commit("setTotal", response.data.total)
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
@@ -6,9 +6,9 @@ export default {
|
||||
},
|
||||
|
||||
actions: {
|
||||
loadNamespaces({ commit }, options) {
|
||||
loadNamespaces({commit}, options) {
|
||||
return Vue.axios.get(`/api/v1/${options.dataType}s/distinct-namespaces`).then(response => {
|
||||
commit('setNamespaces', response.data)
|
||||
commit("setNamespaces", response.data)
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
@@ -7,14 +7,14 @@ export default {
|
||||
plugins: undefined,
|
||||
},
|
||||
actions: {
|
||||
list({ commit }) {
|
||||
return Vue.axios.get(`/api/v1/plugins`).then(response => {
|
||||
commit('setPlugins', response.data)
|
||||
list({commit}) {
|
||||
return Vue.axios.get("/api/v1/plugins").then(response => {
|
||||
commit("setPlugins", response.data)
|
||||
})
|
||||
},
|
||||
load({ commit }, options) {
|
||||
load({commit}, options) {
|
||||
return Vue.axios.get(`/api/v1/plugins/${options.cls}`).then(response => {
|
||||
commit('setPlugin', response.data)
|
||||
commit("setPlugin", response.data)
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
@@ -7,23 +7,23 @@ export default {
|
||||
taskRunDaily: undefined
|
||||
},
|
||||
actions: {
|
||||
dailyGroupByFlow({ commit }, payload) {
|
||||
return Vue.axios.post(`/api/v1/stats/executions/daily/group-by-flow`, payload).then(response => {
|
||||
commit('setDailyGroupByFlow', response.data)
|
||||
dailyGroupByFlow({commit}, payload) {
|
||||
return Vue.axios.post("/api/v1/stats/executions/daily/group-by-flow", payload).then(response => {
|
||||
commit("setDailyGroupByFlow", response.data)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
},
|
||||
daily({ commit }, payload) {
|
||||
return Vue.axios.post(`/api/v1/stats/executions/daily`, payload).then(response => {
|
||||
commit('setDaily', response.data)
|
||||
daily({commit}, payload) {
|
||||
return Vue.axios.post("/api/v1/stats/executions/daily", payload).then(response => {
|
||||
commit("setDaily", response.data)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
},
|
||||
taskRunDaily({ commit }, payload) {
|
||||
return Vue.axios.post(`/api/v1/stats/taskruns/daily`, payload).then(response => {
|
||||
commit('setTaskRunDaily', response.data)
|
||||
taskRunDaily({commit}, payload) {
|
||||
return Vue.axios.post("/api/v1/stats/taskruns/daily", payload).then(response => {
|
||||
commit("setTaskRunDaily", response.data)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import auth from './auth'
|
||||
import core from './core'
|
||||
import execution from './executions'
|
||||
import flow from './flow'
|
||||
import graph from './graph'
|
||||
import layout from './layout'
|
||||
import log from './logs'
|
||||
import namespace from './namespaces'
|
||||
import plugin from './plugins'
|
||||
import settings from './settings'
|
||||
import stat from './stat'
|
||||
import template from './template'
|
||||
import taskrun from './taskruns'
|
||||
import auth from "./auth"
|
||||
import core from "./core"
|
||||
import execution from "./executions"
|
||||
import flow from "./flow"
|
||||
import graph from "./graph"
|
||||
import layout from "./layout"
|
||||
import log from "./logs"
|
||||
import namespace from "./namespaces"
|
||||
import plugin from "./plugins"
|
||||
import settings from "./settings"
|
||||
import stat from "./stat"
|
||||
import template from "./template"
|
||||
import taskrun from "./taskruns"
|
||||
|
||||
export default {
|
||||
modules: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
@@ -7,21 +7,21 @@ export default {
|
||||
maxTaskRunSetting: 100
|
||||
},
|
||||
actions: {
|
||||
findTaskRuns({ commit }, options) {
|
||||
findTaskRuns({commit}, options) {
|
||||
const sort = options.sort
|
||||
delete options.sort
|
||||
let sortQueryString = ''
|
||||
let sortQueryString = ""
|
||||
if (sort) {
|
||||
sortQueryString = `?sort=${sort}`
|
||||
}
|
||||
return Vue.axios.get(`/api/v1/taskruns/search${sortQueryString}`, { params: options }).then(response => {
|
||||
commit('setTaskruns', response.data.results)
|
||||
commit('setTotal', response.data.total)
|
||||
return Vue.axios.get(`/api/v1/taskruns/search${sortQueryString}`, {params: options}).then(response => {
|
||||
commit("setTaskruns", response.data.results)
|
||||
commit("setTotal", response.data.total)
|
||||
})
|
||||
},
|
||||
maxTaskRunSetting({ commit }) {
|
||||
return Vue.axios.get(`/api/v1/taskruns/maxTaskRunSetting`).then(response => {
|
||||
commit('setMaxTaskRunSetting', response.data)
|
||||
maxTaskRunSetting({commit}) {
|
||||
return Vue.axios.get("/api/v1/taskruns/maxTaskRunSetting").then(response => {
|
||||
commit("setMaxTaskRunSetting", response.data)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import Vue from "vue"
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
@@ -8,46 +8,46 @@ export default {
|
||||
},
|
||||
|
||||
actions: {
|
||||
findTemplates({ commit }, options) {
|
||||
const sortString = options.sort ? `?sort=${options.sort}` : ''
|
||||
findTemplates({commit}, options) {
|
||||
const sortString = options.sort ? `?sort=${options.sort}` : ""
|
||||
delete options.sort
|
||||
return Vue.axios.get(`/api/v1/templates/search${sortString}`, {
|
||||
params: options
|
||||
}).then(response => {
|
||||
commit('setTemplates', response.data.results)
|
||||
commit('setTotal', response.data.total)
|
||||
commit("setTemplates", response.data.results)
|
||||
commit("setTotal", response.data.total)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
},
|
||||
loadTemplate({ commit }, options) {
|
||||
loadTemplate({commit}, options) {
|
||||
return Vue.axios.get(`/api/v1/templates/${options.namespace}/${options.id}`).then(response => {
|
||||
commit('setTemplate', response.data)
|
||||
commit("setTemplate", response.data)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
},
|
||||
saveTemplate({ commit }, options) {
|
||||
saveTemplate({commit}, options) {
|
||||
return Vue.axios.put(`/api/v1/templates/${options.template.namespace}/${options.template.id}`, options.template).then(response => {
|
||||
if (response.status >= 300) {
|
||||
return Promise.reject(new Error("Server error on template save"))
|
||||
} else {
|
||||
commit('setTemplate', response.data)
|
||||
commit("setTemplate", response.data)
|
||||
|
||||
return response.data;
|
||||
}
|
||||
})
|
||||
},
|
||||
createTemplate({ commit }, options) {
|
||||
return Vue.axios.post('/api/v1/templates', options.template).then(response => {
|
||||
commit('setTemplate', response.data)
|
||||
createTemplate({commit}, options) {
|
||||
return Vue.axios.post("/api/v1/templates", options.template).then(response => {
|
||||
commit("setTemplate", response.data)
|
||||
|
||||
return response.data;
|
||||
})
|
||||
},
|
||||
deleteTemplate({ commit }, template) {
|
||||
deleteTemplate({commit}, template) {
|
||||
return Vue.axios.delete(`/api/v1/templates/${template.namespace}/${template.id}`).then(() => {
|
||||
commit('setTemplate', null)
|
||||
commit("setTemplate", null)
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,18 +5,18 @@ export function tooltip(tooltipModel) {
|
||||
const bodyLines = (tooltipModel.body || []).map(r => r.lines);
|
||||
|
||||
if (tooltipModel.body) {
|
||||
let innerHtml = '';
|
||||
let innerHtml = "";
|
||||
|
||||
titleLines.forEach(function (title) {
|
||||
innerHtml += '<h6>' + title + '</h6>';
|
||||
innerHtml += "<h6>" + title + "</h6>";
|
||||
});
|
||||
|
||||
bodyLines.forEach(function (body, i) {
|
||||
let colors = tooltipModel.labelColors[i];
|
||||
let style = 'background:' + colors.backgroundColor;
|
||||
style += '; border-color:' + colors.borderColor;
|
||||
let span = '<span class="square" style="' + style + '"></span>';
|
||||
innerHtml += span + body + '<br />';
|
||||
let style = "background:" + colors.backgroundColor;
|
||||
style += "; border-color:" + colors.borderColor;
|
||||
let span = "<span class=\"square\" style=\"" + style + "\"></span>";
|
||||
innerHtml += span + body + "<br />";
|
||||
});
|
||||
|
||||
return innerHtml;
|
||||
@@ -36,7 +36,7 @@ export function defaultConfig(overide) {
|
||||
}
|
||||
},
|
||||
tooltips: {
|
||||
mode: 'index',
|
||||
mode: "index",
|
||||
intersect: false,
|
||||
enabled: false,
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ export function canSaveFlowTemplate(isEdit, user, item, dataType) {
|
||||
|
||||
export function saveFlowTemplate(self, file, dataType) {
|
||||
return self.$store
|
||||
.dispatch(`${dataType}/save${dataType.capitalize()}`, { [dataType]: file })
|
||||
.dispatch(`${dataType}/save${dataType.capitalize()}`, {[dataType]: file})
|
||||
.then((response) => {
|
||||
self.$toast().saved(response.id);
|
||||
})
|
||||
|
||||
@@ -12,5 +12,5 @@ String.prototype.hashCode = function () {
|
||||
hash = ((hash << 5) - hash) + char;
|
||||
hash = hash & hash; // Convert to 32bit integer
|
||||
}
|
||||
return hash + '';
|
||||
return hash + "";
|
||||
}
|
||||
@@ -22,11 +22,11 @@ export default class Markdown {
|
||||
breaks: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
langPrefix: 'language-',
|
||||
quotes: '“”‘’',
|
||||
langPrefix: "language-",
|
||||
quotes: "“”‘’",
|
||||
})
|
||||
|
||||
md.renderer.rules.table_open = () => `<table class="table table-bordered">\n`
|
||||
md.renderer.rules.table_open = () => "<table class=\"table table-bordered\">\n"
|
||||
|
||||
return md.render(
|
||||
markdown
|
||||
|
||||
@@ -34,13 +34,13 @@ export default class QueryBuilder {
|
||||
query.push(QueryBuilder.toLucene(q.q));
|
||||
}
|
||||
|
||||
return query.join(" AND ") || '*'
|
||||
return query.join(" AND ") || "*"
|
||||
}
|
||||
|
||||
static logQueryBuilder(route) {
|
||||
const q = route.query
|
||||
const start = q.start ? iso(q.start) : '*'
|
||||
const end = q.end ? iso(q.end) : '*'
|
||||
const start = q.start ? iso(q.start) : "*"
|
||||
const end = q.end ? iso(q.end) : "*"
|
||||
return [
|
||||
`${q.q ? QueryBuilder.toLucene(q.q) : "*"}`,
|
||||
`timestamp:[${start} TO ${end}]`,
|
||||
|
||||
@@ -58,11 +58,11 @@ export default class State {
|
||||
static icon() {
|
||||
return {
|
||||
[State.CREATED]: "pause-circle-outline",
|
||||
[State.SUCCESS]: 'check-circle-outline',
|
||||
[State.RUNNING]: 'play-circle-outline',
|
||||
[State.FAILED]: 'close-circle-outline',
|
||||
[State.KILLING]: 'close-circle-outline',
|
||||
[State.KILLED]: 'stop-circle-outline'
|
||||
[State.SUCCESS]: "check-circle-outline",
|
||||
[State.RUNNING]: "play-circle-outline",
|
||||
[State.FAILED]: "close-circle-outline",
|
||||
[State.KILLING]: "close-circle-outline",
|
||||
[State.KILLED]: "stop-circle-outline"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ export default {
|
||||
|
||||
return {
|
||||
_wrap: function(message) {
|
||||
return [self.$createElement('span', {domProps: {innerHTML: message}})];
|
||||
return [self.$createElement("span", {domProps: {innerHTML: message}})];
|
||||
},
|
||||
confirm: function(message, callback) {
|
||||
return self.$bvModal
|
||||
|
||||
@@ -12,9 +12,9 @@ export default class Utils {
|
||||
return []
|
||||
.concat(...Object
|
||||
.keys(child)
|
||||
.map(key => typeof child[key] === 'object' ?
|
||||
.map(key => typeof child[key] === "object" ?
|
||||
_flatten(child[key], path.concat([key])) :
|
||||
({ [path.concat([key]).join(".")] : child[key] })
|
||||
({[path.concat([key]).join(".")] : child[key]})
|
||||
)
|
||||
);
|
||||
}(object));
|
||||
@@ -31,12 +31,12 @@ export default class Utils {
|
||||
if (typeof(flat[key]) === "string") {
|
||||
let date = moment(flat[key], moment.ISO_8601);
|
||||
if (date.isValid()) {
|
||||
return {key, value: date.format('LLLL')};
|
||||
return {key, value: date.format("LLLL")};
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof(flat[key]) === "number") {
|
||||
return {key, value: flat[key].toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 ')};
|
||||
return {key, value: flat[key].toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1 ")};
|
||||
}
|
||||
|
||||
return {key, value: flat[key]};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import JsYaml from 'js-yaml';
|
||||
import JsYaml from "js-yaml";
|
||||
import _cloneDeep from "lodash/cloneDeep";
|
||||
|
||||
export default class YamlUtils {
|
||||
@@ -26,7 +26,7 @@ export default class YamlUtils {
|
||||
} else if (typeof(value) === "string" || value instanceof String) {
|
||||
value = value
|
||||
.replaceAll("\t", " ")
|
||||
.replaceAll(/\u00A0/g, ' ');
|
||||
.replaceAll(/\u00A0/g, " ");
|
||||
|
||||
if (value.indexOf("\\n") >= 0) {
|
||||
return value.replaceAll("\\n", "\n") + "\n";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const path = require('path');
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
publicPath: "/ui/",
|
||||
@@ -6,18 +6,14 @@ module.exports = {
|
||||
configureWebpack: {
|
||||
resolve: {
|
||||
alias: {
|
||||
Override: path.resolve(__dirname, 'src/override/')
|
||||
Override: path.resolve(__dirname, "src/override/")
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.sass$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
]
|
||||
use: ["vue-style-loader", "css-loader", "sass-loader"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user