mirror of
https://github.com/kestra-io/kestra.git
synced 2025-12-19 18:05:41 -05:00
feat(ui): refactor the schedule ui
This commit is contained in:
committed by
Ludovic DEHON
parent
8f88d024cb
commit
8a2ffba2d1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"esversion": 6,
|
"esversion": 9,
|
||||||
"asi": true,
|
"asi": true,
|
||||||
"curly": true,
|
"curly": true,
|
||||||
"eqeqeq": true,
|
"eqeqeq": true,
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<span>{{content.message || content}}</span>
|
<span>{{content.message || content}}</span>
|
||||||
<b-table v-if="items && items.length > 0" striped hover :items="items"></b-table>
|
<b-table class="mt-2 mb-0" small bordered v-if="items && items.length > 0" striped hover
|
||||||
|
:items="items"></b-table>
|
||||||
</b-toast>
|
</b-toast>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -51,3 +52,9 @@ export default {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
@import "../styles/variable";
|
||||||
|
table {
|
||||||
|
background-color: $white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -17,8 +17,7 @@
|
|||||||
</template>
|
</template>
|
||||||
</b-table>
|
</b-table>
|
||||||
<div v-if="execution.inputs">
|
<div v-if="execution.inputs">
|
||||||
<hr />
|
<h5>{{$t('inputs')}}</h5>
|
||||||
<h3>{{$t('inputs')}}</h3>
|
|
||||||
<b-table
|
<b-table
|
||||||
responsive="xl"
|
responsive="xl"
|
||||||
striped
|
striped
|
||||||
@@ -41,6 +40,23 @@
|
|||||||
</template>
|
</template>
|
||||||
</b-table>
|
</b-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="variables.length > 0" class="mt-4">
|
||||||
|
<h5>{{$t('variables')}}</h5>
|
||||||
|
<b-table
|
||||||
|
responsive="xl"
|
||||||
|
striped
|
||||||
|
hover
|
||||||
|
bordered
|
||||||
|
:items="this.variables"
|
||||||
|
:fields="fields"
|
||||||
|
class="mb-0"
|
||||||
|
>
|
||||||
|
<template v-slot:cell(key)="row">
|
||||||
|
<code>{{row.item.key}}</code>
|
||||||
|
</template>
|
||||||
|
</b-table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -63,6 +79,14 @@ export default {
|
|||||||
},
|
},
|
||||||
restart() {
|
restart() {
|
||||||
this.$emit("follow");
|
this.$emit("follow");
|
||||||
|
},
|
||||||
|
flat(object) {
|
||||||
|
return Object.assign({}, ...function _flatten(child, path = []) {
|
||||||
|
return [].concat(...Object.keys(child).map(key => typeof child[key] === 'object'
|
||||||
|
? _flatten(child[key], path.concat([key]))
|
||||||
|
: ({ [path.concat([key]).join(".")] : child[key] })
|
||||||
|
));
|
||||||
|
}(object));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
@@ -133,9 +157,24 @@ export default {
|
|||||||
for (const key in this.execution.inputs) {
|
for (const key in this.execution.inputs) {
|
||||||
inputs.push({ key, value: this.execution.inputs[key] });
|
inputs.push({ key, value: this.execution.inputs[key] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(inputs);
|
||||||
return inputs;
|
return inputs;
|
||||||
|
},
|
||||||
|
variables() {
|
||||||
|
const variables = [];
|
||||||
|
|
||||||
|
if (this.execution.variables !== undefined) {
|
||||||
|
const flat = this.flat(this.execution.variables);
|
||||||
|
for (const key in flat) {
|
||||||
|
variables.push({ key, value: flat[key] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return variables;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ import BottomLine from "../layout/BottomLine";
|
|||||||
import RouteContext from "../../mixins/routeContext";
|
import RouteContext from "../../mixins/routeContext";
|
||||||
import permission from "../../models/permission";
|
import permission from "../../models/permission";
|
||||||
import action from "../../models/action";
|
import action from "../../models/action";
|
||||||
|
import { canSaveFlow, saveFlow } from "../../utils/flow";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [RouteContext],
|
mixins: [RouteContext],
|
||||||
@@ -72,13 +73,7 @@ export default {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
canSave() {
|
canSave() {
|
||||||
return (
|
return canSaveFlow(true, this.user, this.content);
|
||||||
this.isEdit && this.user &&
|
|
||||||
this.user.isAllowed(permission.FLOW, action.UPDATE, this.content.namespace)
|
|
||||||
) || (
|
|
||||||
!this.isEdit && this.user &&
|
|
||||||
this.user.isAllowed(permission.FLOW, action.CREATE, this.content.namespace)
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
canDelete() {
|
canDelete() {
|
||||||
return this.isEdit && this.user &&
|
return this.isEdit && this.user &&
|
||||||
@@ -157,13 +152,8 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$store
|
|
||||||
.dispatch("flow/saveFlow", {
|
saveFlow(this, flow)
|
||||||
flow
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
this.$toast().success({message: this.$t("flow update ok")});
|
|
||||||
})
|
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
this.loadFlow();
|
this.loadFlow();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -110,7 +110,14 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.user && this.flow && this.user.isAllowed(permission.EXECUTION, action.UPDATE, this.flow.namespace)) {
|
if (this.user && this.flow && this.user.isAllowed(permission.EXECUTION, action.CREATE, this.flow.namespace)) {
|
||||||
|
tabs.push({
|
||||||
|
tab: "execution-configuration",
|
||||||
|
title: title("trigger")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.user && this.flow && this.user.isAllowed(permission.FLOW, action.UPDATE, this.flow.namespace)) {
|
||||||
tabs.push({
|
tabs.push({
|
||||||
tab: "data-source",
|
tab: "data-source",
|
||||||
title: title("source"),
|
title: title("source"),
|
||||||
@@ -118,11 +125,12 @@ export default {
|
|||||||
});
|
});
|
||||||
|
|
||||||
tabs.push({
|
tabs.push({
|
||||||
tab: "execution-configuration",
|
tab: "schedule",
|
||||||
title: title("trigger")
|
title: title("schedule"),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return tabs;
|
return tabs;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,94 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="flow">
|
<div>
|
||||||
<b-row>
|
<b-list-group>
|
||||||
<b-col md="8">
|
<schedule-item
|
||||||
<b-list-group>
|
@remove="remove"
|
||||||
<schedule-item
|
@set="set"
|
||||||
@remove="remove"
|
:schedule="schedule"
|
||||||
:schedule="schedule"
|
:index="x"
|
||||||
:index="x"
|
v-for="(schedule, x) in (flow.triggers || []) "
|
||||||
v-for="(schedule, x) in triggers"
|
:key="x"
|
||||||
:key="x"
|
/>
|
||||||
/>
|
</b-list-group>
|
||||||
</b-list-group>
|
<bottom-line v-if="canSave">
|
||||||
</b-col>
|
<ul class="navbar-nav ml-auto">
|
||||||
<b-col md="4">
|
<li class="nav-item">
|
||||||
<b-row>
|
<b-button variant="primary" @click="addSchedule" v-if="canSave">
|
||||||
<b-col class="text-center">
|
<plus />
|
||||||
<p>
|
{{ $t('add schedule') }}
|
||||||
<small>Cron helper</small>
|
</b-button>
|
||||||
</p>
|
|
||||||
<b-table responsive :items="cronHelpData"></b-table>
|
<b-button @click="save" v-if="canSave">
|
||||||
<b-table responsive :items="cronHelpTokens"></b-table>
|
<content-save />
|
||||||
</b-col>
|
<span>{{$t('save')}}</span>
|
||||||
</b-row>
|
</b-button>
|
||||||
<b-row>
|
</li>
|
||||||
<b-col>
|
</ul>
|
||||||
<b-form-group>
|
</bottom-line>
|
||||||
<b-btn variant="primary" @click="addSchedule">
|
|
||||||
<plus />
|
|
||||||
{{$t('add schedule') | cap}}
|
|
||||||
</b-btn>
|
|
||||||
</b-form-group>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { mapState } from "vuex";
|
import { mapState } from "vuex";
|
||||||
|
import ContentSave from "vue-material-design-icons/ContentSave";
|
||||||
import Plus from "vue-material-design-icons/Plus";
|
import Plus from "vue-material-design-icons/Plus";
|
||||||
import ScheduleItem from "./ScheduleItem";
|
import ScheduleItem from "./ScheduleItem";
|
||||||
|
import BottomLine from "../layout/BottomLine";
|
||||||
|
import { canSaveFlow, saveFlow } from "../../utils/flow";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Plus,
|
Plus,
|
||||||
ScheduleItem
|
ContentSave,
|
||||||
},
|
ScheduleItem,
|
||||||
watch: {
|
BottomLine
|
||||||
flow() {
|
|
||||||
console.log("on flow change");
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState("flow", ["flow", "triggers"]),
|
...mapState("flow", ["flow"]),
|
||||||
validForm() {
|
...mapState("auth", ["user"]),
|
||||||
return true;
|
canSave() {
|
||||||
},
|
return canSaveFlow(true, this.user, this.flow);
|
||||||
|
|
||||||
cronHelpData() {
|
|
||||||
const helpRecord = {};
|
|
||||||
helpRecord[this.$t("minute")] = "*";
|
|
||||||
helpRecord[this.$t("hour")] = "*";
|
|
||||||
helpRecord[this.$t("day (month)")] = "*";
|
|
||||||
helpRecord[this.$t("month")] = "*";
|
|
||||||
helpRecord[this.$t("day (week)")] = "*";
|
|
||||||
return [helpRecord];
|
|
||||||
},
|
|
||||||
cronHelpTokens() {
|
|
||||||
const helpRecord = {};
|
|
||||||
helpRecord[this.$t("any value")] = "*";
|
|
||||||
helpRecord[this.$t("value list separator")] = ",";
|
|
||||||
helpRecord[this.$t("range of values")] = "-";
|
|
||||||
helpRecord[this.$t("step values")] = "/";
|
|
||||||
return [helpRecord];
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
save() {
|
||||||
|
saveFlow(this, this.flow);
|
||||||
|
},
|
||||||
|
set(index, schedule) {
|
||||||
|
this.$store.commit("flow/setTrigger", {index, trigger: schedule});
|
||||||
|
},
|
||||||
remove(index) {
|
remove(index) {
|
||||||
this.$store.commit("flow/removeTrigger", index);
|
this.$store.commit("flow/removeTrigger", index);
|
||||||
this.$store.dispatch('flow/updateFlowTrigger')
|
|
||||||
},
|
},
|
||||||
addSchedule() {
|
addSchedule() {
|
||||||
this.$store.commit("flow/addTrigger", {
|
this.$store.commit("flow/addTrigger", {
|
||||||
|
id: "schedule",
|
||||||
cron: "0 4 * * 1,4",
|
cron: "0 4 * * 1,4",
|
||||||
type: "org.kestra.core.models.triggers.types.Schedule"
|
type: "org.kestra.core.models.triggers.types.Schedule",
|
||||||
});
|
});
|
||||||
this.$store.dispatch('flow/updateFlowTrigger')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,34 +1,73 @@
|
|||||||
<template>
|
<template>
|
||||||
<b-list-group-item>
|
<b-list-group-item>
|
||||||
<b-row>
|
<b-form-group label-cols-sm="3" label-cols-lg="2" :label="$t('id')" :label-for="'input-id-' + index">
|
||||||
<b-col md="6">
|
<b-form-input required :id="'input-id-' + index" v-model="schedule.id"></b-form-input>
|
||||||
{{schedule.cron}}
|
</b-form-group>
|
||||||
<b-form-group>
|
|
||||||
<b-input type="text" v-model="schedule.cron" />
|
<b-form-group label-cols-sm="3" label-cols-lg="2"
|
||||||
</b-form-group>
|
:label-for="'input-cron-' + index"
|
||||||
<p class="text-danger" v-if="!isValid">{{$t('invalid schedule')}}</p>
|
:state="isValid">
|
||||||
<p class="text-primary" v-else>{{cronHumanReadable}}</p>
|
<template v-slot:label>
|
||||||
<b-btn variant="warning" @click="remove">
|
{{ $t('schedules.cron.expression')}}
|
||||||
<delete />Remove
|
<b-link class="text-body" :id="'tooltip-' + index">
|
||||||
</b-btn>
|
<help />
|
||||||
</b-col>
|
</b-link>
|
||||||
<b-col md="6" class="text-center">
|
|
||||||
<div v-if="occurences.length">
|
<b-tooltip :target="'tooltip-' + index" placement="bottom">
|
||||||
<p class="font-weight-bold">3 Next occurences</p>
|
<div v-if="isValid">
|
||||||
<p v-for="(occurence, x) in occurences" :key="x">{{occurence | date('LLL:ss')}}</p>
|
<p class="font-weight-bold">3 Next occurences</p>
|
||||||
</div>
|
|
||||||
</b-col>
|
<span v-if="occurences.length">
|
||||||
</b-row>
|
<span v-for="(occurence, x) in occurences" :key="x">{{occurence | date('LLL:ss')}}<br /></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span v-else>
|
||||||
|
{{ $t("schedules.cron.invalid") }}
|
||||||
|
</span>
|
||||||
|
</b-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<b-form-input required :id="'input-cron-' + index" v-model="schedule.cron"></b-form-input>
|
||||||
|
|
||||||
|
<b-form-invalid-feedback>
|
||||||
|
Enter at least 3 letters
|
||||||
|
</b-form-invalid-feedback>
|
||||||
|
|
||||||
|
<b-form-text>{{ cronHumanReadable }}</b-form-text>
|
||||||
|
</b-form-group>
|
||||||
|
|
||||||
|
|
||||||
|
<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
|
||||||
|
</b-btn>
|
||||||
|
|
||||||
|
</b-form-group>
|
||||||
</b-list-group-item>
|
</b-list-group-item>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
const cronstrue = require("cronstrue/i18n");
|
const cronstrue = require("cronstrue/i18n");
|
||||||
const cronParser = require("cron-parser");
|
const cronParser = require("cron-parser");
|
||||||
import Delete from "vue-material-design-icons/Delete";
|
import Delete from "vue-material-design-icons/Delete";
|
||||||
|
import Help from "vue-material-design-icons/HelpBox";
|
||||||
|
import DatePicker from "vue2-datepicker";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Delete
|
Delete,
|
||||||
|
Help,
|
||||||
|
DatePicker
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
schedule: {
|
schedule: {
|
||||||
@@ -40,7 +79,26 @@ export default {
|
|||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
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() {
|
occurences() {
|
||||||
const occurences = [];
|
const occurences = [];
|
||||||
if (!this.isValid) {
|
if (!this.isValid) {
|
||||||
@@ -57,7 +115,7 @@ export default {
|
|||||||
try {
|
try {
|
||||||
return cronstrue.toString(this.schedule.cron, { locale });
|
return cronstrue.toString(this.schedule.cron, { locale });
|
||||||
} catch {
|
} catch {
|
||||||
return "invalid cron expression";
|
return this.$t("schedules.cron.invalid");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isValid() {
|
isValid() {
|
||||||
@@ -66,17 +124,8 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
remove() {
|
remove() {
|
||||||
this.$bvModal
|
this.$emit("remove", this.index);
|
||||||
.msgBoxConfirm(this.$t("Are you sure?"))
|
|
||||||
.then(value => {
|
|
||||||
if (value) {
|
|
||||||
this.$emit("remove", this.index);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onChange() {
|
|
||||||
this.schedule.cron = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,6 +12,15 @@
|
|||||||
|
|
||||||
@import '~vue2-datepicker/scss/index';
|
@import '~vue2-datepicker/scss/index';
|
||||||
|
|
||||||
|
.mx-input {
|
||||||
|
border-radius: $border-radius;
|
||||||
|
}
|
||||||
|
.form-group {
|
||||||
|
.mx-datepicker {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@import 'styles/vue-material-custom';
|
@import 'styles/vue-material-custom';
|
||||||
|
|
||||||
@import '~c3/src/scss/main';
|
@import '~c3/src/scss/main';
|
||||||
@@ -23,4 +32,4 @@
|
|||||||
select {
|
select {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ export default {
|
|||||||
flow: undefined,
|
flow: undefined,
|
||||||
total: 0,
|
total: 0,
|
||||||
dataTree: undefined,
|
dataTree: undefined,
|
||||||
triggers: []
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
@@ -55,26 +54,60 @@ export default {
|
|||||||
return Vue.axios.get(`/api/v1/flows/${flow.namespace}/${flow.id}/tree`).then(response => {
|
return Vue.axios.get(`/api/v1/flows/${flow.namespace}/${flow.id}/tree`).then(response => {
|
||||||
commit('setDataTree', response.data.tasks)
|
commit('setDataTree', response.data.tasks)
|
||||||
})
|
})
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setFlows(state, flows) {
|
setFlows(state, flows) {
|
||||||
state.flows = flows
|
state.flows = flows
|
||||||
},
|
},
|
||||||
setFlow(state, flow) {
|
setFlow(state, flow) {
|
||||||
state.flow = flow
|
if (flow.triggers !== undefined) {
|
||||||
if (flow.triggers) {
|
flow.triggers = flow.triggers.map(trigger => {
|
||||||
state.triggers = flow.triggers
|
if (trigger.backfill === undefined) {
|
||||||
|
trigger.backfill = {
|
||||||
|
start: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trigger;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state.flow = {...flow}
|
||||||
},
|
},
|
||||||
setTriggers(state, triggers) {
|
setTrigger(state, {index, trigger}) {
|
||||||
state.triggers = triggers
|
let flow = state.flow;
|
||||||
|
|
||||||
|
if (flow.triggers === undefined) {
|
||||||
|
flow.triggers = []
|
||||||
|
}
|
||||||
|
|
||||||
|
flow.triggers[index] = trigger;
|
||||||
|
|
||||||
|
state.flow = {...flow}
|
||||||
},
|
},
|
||||||
removeTrigger(state, index) {
|
removeTrigger(state, index) {
|
||||||
state.triggers.splice(index, 1);
|
let flow = state.flow;
|
||||||
|
flow.triggers.splice(index, 1);
|
||||||
|
|
||||||
|
state.flow = {...flow}
|
||||||
},
|
},
|
||||||
addTrigger(state, trigger) {
|
addTrigger(state, trigger) {
|
||||||
state.triggers.push(trigger)
|
let flow = state.flow;
|
||||||
|
|
||||||
|
if (trigger.backfill === undefined) {
|
||||||
|
trigger.backfill = {
|
||||||
|
start: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flow.triggers === undefined) {
|
||||||
|
flow.triggers = []
|
||||||
|
}
|
||||||
|
|
||||||
|
flow.triggers.push(trigger)
|
||||||
|
|
||||||
|
state.flow = {...flow}
|
||||||
},
|
},
|
||||||
setTotal(state, total) {
|
setTotal(state, total) {
|
||||||
state.total = total
|
state.total = total
|
||||||
@@ -86,9 +119,7 @@ export default {
|
|||||||
getters: {
|
getters: {
|
||||||
flow (state) {
|
flow (state) {
|
||||||
if (state.flow) {
|
if (state.flow) {
|
||||||
const flow = state.flow
|
return state.flow;
|
||||||
flow.triggers = state.triggers
|
|
||||||
return flow
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,116 +5,116 @@
|
|||||||
// Color system
|
// Color system
|
||||||
//
|
//
|
||||||
|
|
||||||
$white: #fff !default;
|
$white: #fff;
|
||||||
$gray-100: #f8f9fa !default;
|
$gray-100: #f8f9fa;
|
||||||
$gray-200: #eee !default;
|
$gray-200: #eee;
|
||||||
$gray-300: #dee2e6 !default;
|
$gray-300: #dee2e6;
|
||||||
$gray-400: #ccc !default;
|
$gray-400: #ccc;
|
||||||
$gray-500: #adb5bd !default;
|
$gray-500: #adb5bd;
|
||||||
$gray-600: #888 !default;
|
$gray-600: #888;
|
||||||
$gray-700: #495057 !default;
|
$gray-700: #495057;
|
||||||
$gray-800: #333 !default;
|
$gray-800: #333;
|
||||||
$gray-900: #222 !default;
|
$gray-900: #222;
|
||||||
$black: #000 !default;
|
$black: #000;
|
||||||
|
|
||||||
$blue: #1AA5DE !default;
|
$blue: #1AA5DE;
|
||||||
$indigo: #6610f2 !default;
|
$indigo: #6610f2;
|
||||||
$purple: #6f42c1 !default;
|
$purple: #6f42c1;
|
||||||
$pink: #e83e8c !default;
|
$pink: #e83e8c;
|
||||||
$red: #F04124 !default;
|
$red: #F04124;
|
||||||
$orange: #fd7e14 !default;
|
$orange: #fd7e14;
|
||||||
$yellow: #FBD10B !default;
|
$yellow: #FBD10B;
|
||||||
$green: #43ac6a !default;
|
$green: #43ac6a;
|
||||||
$teal: #1DBAAF !default;
|
$teal: #1DBAAF;
|
||||||
$cyan: #5bc0de !default;
|
$cyan: #5bc0de;
|
||||||
|
|
||||||
$primary: $blue !default;
|
$primary: $blue;
|
||||||
$secondary: $teal !default;
|
$secondary: $teal;
|
||||||
$tertiary: $yellow;
|
$tertiary: $yellow;
|
||||||
$success: $green !default;
|
$success: $green;
|
||||||
$info: $cyan !default;
|
$info: $cyan;
|
||||||
$warning: $yellow !default;
|
$warning: $yellow;
|
||||||
$danger: $red !default;
|
$danger: $red;
|
||||||
$light: $gray-200 !default;
|
$light: $gray-200;
|
||||||
$dark: $gray-900 !default;
|
$dark: $gray-900;
|
||||||
|
|
||||||
$yiq-contrasted-threshold: 200 !default;
|
$yiq-contrasted-threshold: 200;
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
|
||||||
$border-radius: 0px !default;
|
$border-radius: 0px;
|
||||||
$border-radius-lg: 4px !default;
|
$border-radius-lg: 4px;
|
||||||
$border-radius-sm: 0px !default;
|
$border-radius-sm: 0px;
|
||||||
|
|
||||||
// Fonts
|
// Fonts
|
||||||
|
|
||||||
$font-family-sans-serif: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !default;
|
$font-family-sans-serif: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
$font-family-monospace: 'Source Code Pro', SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !default;
|
$font-family-monospace: 'Source Code Pro', SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||||
|
|
||||||
$font-size-base: 1rem !default;
|
$font-size-base: 1rem;
|
||||||
$font-size-xs: $font-size-base * .75 !default;
|
$font-size-xs: $font-size-base * .75;
|
||||||
|
|
||||||
$headings-font-weight: 300 !default;
|
$headings-font-weight: 300;
|
||||||
$text-muted: $gray-700 !default;
|
$text-muted: $gray-700;
|
||||||
|
|
||||||
// Buttons
|
// Buttons
|
||||||
|
|
||||||
$input-btn-padding-x: 0.5rem !default;
|
$input-btn-padding-x: 0.5rem;
|
||||||
|
|
||||||
$btn-font-weight: 300 !default;
|
$btn-font-weight: 300;
|
||||||
|
|
||||||
// Dropdowns
|
// Dropdowns
|
||||||
|
|
||||||
$dropdown-border-color: rgba($black, .1) !default;
|
$dropdown-border-color: rgba($black, .1);
|
||||||
$dropdown-divider-bg: rgba($black, .1) !default;
|
$dropdown-divider-bg: rgba($black, .1);
|
||||||
|
|
||||||
// Navs
|
// Navs
|
||||||
|
|
||||||
$nav-link-disabled-color: $gray-400 !default;
|
$nav-link-disabled-color: $gray-400;
|
||||||
|
|
||||||
$nav-tabs-border-color: $dropdown-border-color !default;
|
$nav-tabs-border-color: $dropdown-border-color;
|
||||||
$nav-tabs-link-hover-border-color: $nav-tabs-border-color !default;
|
$nav-tabs-link-hover-border-color: $nav-tabs-border-color;
|
||||||
$nav-tabs-link-active-border-color: $nav-tabs-border-color !default;
|
$nav-tabs-link-active-border-color: $nav-tabs-border-color;
|
||||||
|
|
||||||
// Navbar
|
// Navbar
|
||||||
|
|
||||||
$navbar-dark-color: rgba($white, .7) !default;
|
$navbar-dark-color: rgba($white, .7);
|
||||||
$navbar-dark-hover-color: $white !default;
|
$navbar-dark-hover-color: $white;
|
||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
|
|
||||||
$pagination-color: $gray-600 !default;
|
$pagination-color: $gray-600;
|
||||||
$pagination-border-color: $nav-tabs-border-color !default;
|
$pagination-border-color: $nav-tabs-border-color;
|
||||||
|
|
||||||
$pagination-active-border-color: darken($primary, 5%) !default;
|
$pagination-active-border-color: darken($primary, 5%);
|
||||||
|
|
||||||
|
|
||||||
// Jumbotron
|
// Jumbotron
|
||||||
|
|
||||||
$jumbotron-padding: 4rem !default;
|
$jumbotron-padding: 4rem;
|
||||||
|
|
||||||
// Cards
|
// Cards
|
||||||
|
|
||||||
$card-inner-border-radius: 0px !default;
|
$card-inner-border-radius: 0px;
|
||||||
|
|
||||||
// Badges
|
// Badges
|
||||||
|
|
||||||
$badge-font-weight: 300 !default;
|
$badge-font-weight: 300;
|
||||||
$badge-padding-x: 1rem !default;
|
$badge-padding-x: 1rem;
|
||||||
|
|
||||||
// Progress bars
|
// Progress bars
|
||||||
|
|
||||||
$progress-bg: $gray-400 !default;
|
$progress-bg: $gray-400;
|
||||||
$progress-bar-color: $white !default;
|
$progress-bar-color: $white;
|
||||||
|
|
||||||
// List group
|
// List group
|
||||||
|
|
||||||
$list-group-disabled-bg: $gray-200 !default;
|
$list-group-disabled-bg: $gray-200;
|
||||||
|
|
||||||
// Close
|
// Close
|
||||||
|
|
||||||
$close-color: $gray-600 !default;
|
$close-color: $gray-600;
|
||||||
$close-text-shadow: none !default;
|
$close-text-shadow: none;
|
||||||
|
|
||||||
// Breadcrumb
|
// Breadcrumb
|
||||||
$breadcrumb-item-padding: 0.25rem;
|
$breadcrumb-item-padding: 0.25rem;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"en": {
|
"en": {
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"namespace": "namespace",
|
"namespace": "Namespace",
|
||||||
"revision": "Revision",
|
"revision": "Revision",
|
||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
"Set default page": "Set default page",
|
"Set default page": "Set default page",
|
||||||
@@ -96,10 +96,18 @@
|
|||||||
"down trend": "Decreasing",
|
"down trend": "Decreasing",
|
||||||
"flow update aborted": "Flow update aborted",
|
"flow update aborted": "Flow update aborted",
|
||||||
"invalid flow": "Invalid flow",
|
"invalid flow": "Invalid flow",
|
||||||
"add schedule": "add schedule",
|
"add schedule": "Add a schedule",
|
||||||
"schedule": "schedule",
|
"schedule": "Schedule",
|
||||||
|
"schedules": {
|
||||||
|
"cron": {
|
||||||
|
"expression": "Expression",
|
||||||
|
"backfilll": "Backfill",
|
||||||
|
"invalid": "Invalid cron expression"
|
||||||
|
}
|
||||||
|
},
|
||||||
"inputs": "Inputs",
|
"inputs": "Inputs",
|
||||||
"input": "Input",
|
"input": "Input",
|
||||||
|
"variables": "Variables",
|
||||||
"download": "Download"
|
"download": "Download"
|
||||||
},
|
},
|
||||||
"fr": {
|
"fr": {
|
||||||
@@ -200,10 +208,18 @@
|
|||||||
"down trend": "En baisse",
|
"down trend": "En baisse",
|
||||||
"flow update aborted": "Mise à jour du flow annulée",
|
"flow update aborted": "Mise à jour du flow annulée",
|
||||||
"invalid flow": "Flow invalide",
|
"invalid flow": "Flow invalide",
|
||||||
"add schedule": "ajouter récurrence",
|
"add schedule": "Ajouter un plannificationS",
|
||||||
"schedule": "tâche programmée",
|
"schedule": "Tâche plannifié",
|
||||||
|
"schedules": {
|
||||||
|
"cron": {
|
||||||
|
"expression": "Expression",
|
||||||
|
"backfilll": "Remblayage",
|
||||||
|
"invalid": "cron expression invalide"
|
||||||
|
}
|
||||||
|
},
|
||||||
"inputs": "Entrées",
|
"inputs": "Entrées",
|
||||||
"input": "Entré",
|
"input": "Entré",
|
||||||
|
"variables": "Variables",
|
||||||
"download": "Télécharger"
|
"download": "Télécharger"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
22
ui/src/utils/flow.js
Normal file
22
ui/src/utils/flow.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import permission from "../models/permission";
|
||||||
|
import action from "../models/action";
|
||||||
|
|
||||||
|
export function canSaveFlow(isEdit, user, flow) {
|
||||||
|
return (
|
||||||
|
isEdit && user &&
|
||||||
|
user.isAllowed(permission.FLOW, action.UPDATE, flow.namespace)
|
||||||
|
) || (
|
||||||
|
!isEdit && user &&
|
||||||
|
user.isAllowed(permission.FLOW, action.CREATE, flow.namespace)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveFlow(self, flow) {
|
||||||
|
return self.$store
|
||||||
|
.dispatch("flow/saveFlow", {
|
||||||
|
flow
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
self.$toast().success({message: self.$t("flow update ok")});
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user