run/task: show live timer
This commit is contained in:
parent
bcc00a3543
commit
6996917ff2
|
@ -135,7 +135,7 @@ import { userLocalRunLink, projectRunLink } from "@/util/link.js";
|
|||
import { runStatus, runResultClass } from "@/util/run.js";
|
||||
|
||||
export default {
|
||||
name: "RunDetail",
|
||||
name: "rundetail",
|
||||
directives: {
|
||||
clickOutside: vClickOutside.directive
|
||||
},
|
||||
|
|
|
@ -7,41 +7,19 @@
|
|||
>
|
||||
<div>{{ fetchRunError }}</div>
|
||||
</div>
|
||||
<RunDetail :run="run" :ownertype="ownertype" :ownername="ownername" :projectref="projectref"/>
|
||||
<rundetail :run="run" :ownertype="ownertype" :ownername="ownername" :projectref="projectref"/>
|
||||
<div v-if="run">
|
||||
<div v-if="run.phase != 'setuperror'">
|
||||
<div class="m-4 text-xl font-bold">Tasks</div>
|
||||
|
||||
<ul v-if="run">
|
||||
<li
|
||||
class="mb-2 border-l-5 rounded-l"
|
||||
v-for="task in run.sortedTasks"
|
||||
v-bind:key="task.id"
|
||||
:class="taskClass(task)"
|
||||
>
|
||||
<div class="pl-4 py-4 flex justify-between items-center border border-l-0 rounded-r">
|
||||
<router-link class="w-1/3 font-bold" tag="a" :to="runTaskLink(task)">
|
||||
<span class="w-1/3 font-bold">{{task.name}}</span>
|
||||
</router-link>
|
||||
<div class="column">
|
||||
<span
|
||||
class="tag"
|
||||
v-if="run.tasks_waiting_approval.includes(task.id)"
|
||||
>Waiting approval</span>
|
||||
</div>
|
||||
<div class="w-40">
|
||||
<span class="block" v-if="parents(task).length > 0">depends on: </span>
|
||||
<span
|
||||
class="font-thin text-gray-600"
|
||||
v-for="dep in parents(task)"
|
||||
v-bind:key="dep"
|
||||
>{{dep}}</span>
|
||||
</div>
|
||||
<!-- <span
|
||||
class="duration"
|
||||
v-if="duration && (step.Phase == 'success' || step.Phase == 'failed') "
|
||||
>{{duration}}</span>-->
|
||||
</div>
|
||||
<li v-for="task in run.sortedTasks" v-bind:key="task.id">
|
||||
<task
|
||||
v-bind:task="task"
|
||||
v-bind:link="runTaskLink(task)"
|
||||
v-bind:waiting-approval="run.tasks_waiting_approval.includes(task.id)"
|
||||
v-bind:parents="parents(task)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -63,11 +41,12 @@
|
|||
import { fetchRun } from "@/util/data.js";
|
||||
import { userLocalRunTaskLink, projectRunTaskLink } from "@/util/link.js";
|
||||
|
||||
import RunDetail from "@/components/rundetail.vue";
|
||||
import rundetail from "@/components/rundetail.vue";
|
||||
import task from "@/components/task.vue";
|
||||
|
||||
export default {
|
||||
name: "run",
|
||||
components: { RunDetail },
|
||||
name: "runsummary",
|
||||
components: { rundetail, task },
|
||||
props: {
|
||||
ownertype: String,
|
||||
ownername: String,
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
class="mb-2 border-l-5 rounded-l"
|
||||
:class="stepClass(step)"
|
||||
:class="stepClass"
|
||||
role="tab"
|
||||
:aria-expanded="active ? 'true' : 'false'"
|
||||
>
|
||||
|
@ -14,7 +14,7 @@
|
|||
></i>
|
||||
<span class="w-1/3 font-bold">{{step.name}}</span>
|
||||
</div>
|
||||
<span class>{{duration}}</span>
|
||||
<span class>{{ duration }}</span>
|
||||
</div>
|
||||
<div class="p-1 log-container" v-show="active">
|
||||
<Log
|
||||
|
@ -38,14 +38,14 @@ import Log from "@/components/log.vue";
|
|||
momentDurationFormatSetup(moment);
|
||||
|
||||
export default {
|
||||
name: "Collapse",
|
||||
name: "step",
|
||||
components: {
|
||||
Log
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: false,
|
||||
duration: null
|
||||
now: moment()
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -55,42 +55,43 @@ export default {
|
|||
stepnum: Number,
|
||||
step: Object
|
||||
},
|
||||
created() {
|
||||
this.updateDuration(this.step);
|
||||
},
|
||||
ready() {
|
||||
if (this.active) {
|
||||
this.$emit("collapse-open", this.index);
|
||||
computed: {
|
||||
duration() {
|
||||
let formatString = "h:mm:ss[s]";
|
||||
let start = moment(this.step.start_time);
|
||||
let end = moment(this.step.end_time);
|
||||
|
||||
if (this.step.start_time === null) {
|
||||
return moment.duration(0).format(formatString);
|
||||
}
|
||||
if (this.step.end_time === null) {
|
||||
return moment.duration(this.now.diff(start)).format(formatString);
|
||||
}
|
||||
return moment.duration(end.diff(start)).format(formatString);
|
||||
},
|
||||
watch: {
|
||||
step: function(step) {
|
||||
this.updateDuration(step);
|
||||
stepClass() {
|
||||
if (this.step.phase == "success") return "success";
|
||||
if (this.step.phase == "failed") return "failed";
|
||||
if (this.step.phase == "stopped") return "failed";
|
||||
if (this.step.phase == "running") return "running";
|
||||
return "unknown";
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
stepClass(step) {
|
||||
if (step.phase == "success") return "success";
|
||||
if (step.phase == "failed") return "failed";
|
||||
if (step.phase == "stopped") return "failed";
|
||||
if (step.phase == "running") return "running";
|
||||
return "unknown";
|
||||
},
|
||||
updateDuration(step) {
|
||||
let start = moment(step.start_time);
|
||||
let end = moment(step.end_time);
|
||||
if (start === null || end === null) {
|
||||
this.duration = null;
|
||||
return;
|
||||
}
|
||||
this.duration = moment.duration(end.diff(start)).format("h:mm:ss[s]");
|
||||
},
|
||||
toggle() {
|
||||
this.active = !this.active;
|
||||
if (this.active) {
|
||||
this.$emit("collapse-open", this.index);
|
||||
this.$emit("step-open", this.index);
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.active) {
|
||||
this.$emit("step-open", this.index);
|
||||
}
|
||||
window.setInterval(() => {
|
||||
this.now = moment();
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
</script>
|
|
@ -1,117 +1,69 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="fetchRunError || fetchTaskError"
|
||||
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
|
||||
role="alert"
|
||||
>
|
||||
<div>Error fetching Run: {{ fetchRunError }}</div>
|
||||
<div>Error fetching Task: {{ fetchTaskError }}</div>
|
||||
<div class="mb-2 border-l-5 rounded-l" :class="taskClass(task)">
|
||||
<div class="px-4 py-4 flex justify-between items-center border border-l-0 rounded-r">
|
||||
<router-link class="w-1/3 font-bold" tag="a" :to="link">
|
||||
<span class="w-1/3 font-bold">{{task.name}}</span>
|
||||
</router-link>
|
||||
<div class="w-1/4">
|
||||
<span class="tag" v-if="waitingApproval">Waiting approval</span>
|
||||
</div>
|
||||
<RunDetail :run="run" :ownertype="ownertype" :ownername="ownername" :projectref="projectref"/>
|
||||
<div v-if="task != null">
|
||||
<div class="mt-8 mb-4 flex justify-between items-center">
|
||||
<div class="flex items-center">
|
||||
<span class="text-2xl mr-3">{{task.name}}</span>
|
||||
|
||||
<span
|
||||
class="mr-3 rounded px-2 py-1 text-xs"
|
||||
:class="taskClass(task)"
|
||||
>{{ task.status | capitalize }}</span>
|
||||
</div>
|
||||
<button
|
||||
v-if="task.waiting_approval"
|
||||
class="btn btn-blue"
|
||||
@click="approveTask(run.id, task.id)"
|
||||
>Approve</button>
|
||||
</div>
|
||||
<Collapse
|
||||
v-bind:runid="runid"
|
||||
v-bind:taskid="taskid"
|
||||
v-bind:setup="true"
|
||||
v-bind:step="task.setup_step"
|
||||
/>
|
||||
<div v-for="(step, index) in task.steps" v-bind:key="index">
|
||||
<Collapse
|
||||
v-bind:runid="runid"
|
||||
v-bind:taskid="taskid"
|
||||
v-bind:stepnum="index"
|
||||
v-bind:step="step"
|
||||
/>
|
||||
<div class="w-1/4">
|
||||
<span class="block" v-if="parents.length > 0">depends on: </span>
|
||||
<span class="font-thin text-gray-600" v-for="dep in parents" v-bind:key="dep">{{dep}}</span>
|
||||
</div>
|
||||
<span class="w-16 text-right">{{ duration }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fetchRun, fetchTask, approveTask } from "@/util/data.js";
|
||||
import * as moment from "moment";
|
||||
import momentDurationFormatSetup from "moment-duration-format";
|
||||
|
||||
import Collapse from "@/components/collapse.vue";
|
||||
import RunDetail from "@/components/rundetail.vue";
|
||||
momentDurationFormatSetup(moment);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Collapse,
|
||||
RunDetail
|
||||
},
|
||||
name: "task",
|
||||
props: {
|
||||
ownertype: String,
|
||||
ownername: String,
|
||||
projectref: Array,
|
||||
runid: String,
|
||||
taskid: String
|
||||
},
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
fetchRunError: null,
|
||||
fetchTaskError: null,
|
||||
run: null,
|
||||
task: null,
|
||||
polling: null
|
||||
now: moment()
|
||||
};
|
||||
},
|
||||
props: {
|
||||
task: Object,
|
||||
link: Object,
|
||||
waitingApproval: Boolean,
|
||||
parents: Array
|
||||
},
|
||||
computed: {
|
||||
duration() {
|
||||
let formatString = "h:mm:ss[s]";
|
||||
let start = moment(this.task.start_time);
|
||||
let end = moment(this.task.end_time);
|
||||
|
||||
if (this.task.start_time === null) {
|
||||
return moment.duration(0).format(formatString);
|
||||
}
|
||||
if (this.task.end_time === null) {
|
||||
return moment.duration(this.now.diff(start)).format(formatString);
|
||||
}
|
||||
return moment.duration(end.diff(start)).format(formatString);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
taskClass(task) {
|
||||
if (task.status == "success") return "is-success";
|
||||
if (task.status == "failed") return "is-failed";
|
||||
if (task.status == "stopped") return "is-failed";
|
||||
if (task.status == "running") return "is-running";
|
||||
if (task.status == "success") return "success";
|
||||
if (task.status == "failed") return "failed";
|
||||
if (task.status == "stopped") return "failed";
|
||||
if (task.status == "running") return "running";
|
||||
return "unknown";
|
||||
},
|
||||
async fetchRun() {
|
||||
let { data, error } = await fetchRun(this.runid);
|
||||
if (error) {
|
||||
this.fetchRunError = error;
|
||||
return;
|
||||
}
|
||||
this.fetchRunError = error;
|
||||
this.run = data;
|
||||
},
|
||||
async fetchTask() {
|
||||
let { data, error } = await fetchTask(this.runid, this.taskid);
|
||||
if (error) {
|
||||
this.fetchTaskError = error;
|
||||
return;
|
||||
}
|
||||
this.fetchTaskError = error;
|
||||
this.task = data;
|
||||
},
|
||||
pollData() {
|
||||
this.polling = setInterval(() => {
|
||||
this.fetchTask();
|
||||
this.fetchRun();
|
||||
}, 2000);
|
||||
},
|
||||
approveTask: approveTask
|
||||
},
|
||||
created: function() {
|
||||
this.fetchRun();
|
||||
this.fetchTask();
|
||||
this.pollData();
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.polling);
|
||||
created() {
|
||||
window.setInterval(() => {
|
||||
this.now = moment();
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="fetchRunError || fetchTaskError"
|
||||
class="mb-10 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
|
||||
role="alert"
|
||||
>
|
||||
<div>Error fetching Run: {{ fetchRunError }}</div>
|
||||
<div>Error fetching Task: {{ fetchTaskError }}</div>
|
||||
</div>
|
||||
<rundetail :run="run" :ownertype="ownertype" :ownername="ownername" :projectref="projectref"/>
|
||||
<div v-if="task != null">
|
||||
<div class="mt-8 mb-4 flex justify-between items-center">
|
||||
<div class="flex items-center">
|
||||
<span class="text-2xl mr-3">{{task.name}}</span>
|
||||
|
||||
<span
|
||||
class="mr-3 rounded px-2 py-1 text-xs"
|
||||
:class="taskClass(task)"
|
||||
>{{ task.status | capitalize }}</span>
|
||||
</div>
|
||||
<button
|
||||
v-if="task.waiting_approval"
|
||||
class="btn btn-blue"
|
||||
@click="approveTask(run.id, task.id)"
|
||||
>Approve</button>
|
||||
</div>
|
||||
<step
|
||||
v-bind:runid="runid"
|
||||
v-bind:taskid="taskid"
|
||||
v-bind:setup="true"
|
||||
v-bind:step="task.setup_step"
|
||||
/>
|
||||
<div v-for="(step, index) in task.steps" v-bind:key="index">
|
||||
<step
|
||||
v-bind:runid="runid"
|
||||
v-bind:taskid="taskid"
|
||||
v-bind:stepnum="index"
|
||||
v-bind:step="step"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { fetchRun, fetchTask, approveTask } from "@/util/data.js";
|
||||
|
||||
import step from "@/components/step.vue";
|
||||
import rundetail from "@/components/rundetail.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
step,
|
||||
rundetail
|
||||
},
|
||||
name: "tasksummary",
|
||||
props: {
|
||||
ownertype: String,
|
||||
ownername: String,
|
||||
projectref: Array,
|
||||
runid: String,
|
||||
taskid: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fetchRunError: null,
|
||||
fetchTaskError: null,
|
||||
run: null,
|
||||
task: null,
|
||||
polling: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
taskClass(task) {
|
||||
if (task.status == "success") return "is-success";
|
||||
if (task.status == "failed") return "is-failed";
|
||||
if (task.status == "stopped") return "is-failed";
|
||||
if (task.status == "running") return "is-running";
|
||||
return "unknown";
|
||||
},
|
||||
async fetchRun() {
|
||||
let { data, error } = await fetchRun(this.runid);
|
||||
if (error) {
|
||||
this.fetchRunError = error;
|
||||
return;
|
||||
}
|
||||
this.fetchRunError = error;
|
||||
this.run = data;
|
||||
},
|
||||
async fetchTask() {
|
||||
let { data, error } = await fetchTask(this.runid, this.taskid);
|
||||
if (error) {
|
||||
this.fetchTaskError = error;
|
||||
return;
|
||||
}
|
||||
this.fetchTaskError = error;
|
||||
this.task = data;
|
||||
},
|
||||
pollData() {
|
||||
this.polling = setInterval(() => {
|
||||
this.fetchTask();
|
||||
this.fetchRun();
|
||||
}, 2000);
|
||||
},
|
||||
approveTask: approveTask
|
||||
},
|
||||
created: function() {
|
||||
this.fetchRun();
|
||||
this.fetchTask();
|
||||
this.pollData();
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.polling);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
</style>
|
|
@ -15,8 +15,8 @@ import createprojectgroup from "./components/createprojectgroup.vue";
|
|||
import createorganization from "./components/createorganization.vue";
|
||||
import orgmembers from "./components/orgmembers.vue";
|
||||
import runs from "./components/runs.vue";
|
||||
import run from "./components/run.vue";
|
||||
import task from "./components/task.vue";
|
||||
import runsummary from "./components/runsummary.vue";
|
||||
import tasksummary from "./components/tasksummary.vue";
|
||||
import Oauth2 from "./views/Oauth2.vue";
|
||||
import Register from "./views/Register.vue";
|
||||
import Login from "./views/Login.vue";
|
||||
|
@ -87,13 +87,13 @@ const router = new VueRouter({
|
|||
{
|
||||
path: "runs/:runid",
|
||||
name: "user local run",
|
||||
component: run,
|
||||
component: runsummary,
|
||||
props: (route) => ({ ownertype: "user", ownername: route.params.username, runid: route.params.runid })
|
||||
},
|
||||
{
|
||||
path: "runs/:runid/tasks/:taskid",
|
||||
name: "user local run task",
|
||||
component: task,
|
||||
component: tasksummary,
|
||||
props: (route) => ({ ownertype: "user", ownername: route.params.username, runid: route.params.runid, taskid: route.params.taskid })
|
||||
},
|
||||
{
|
||||
|
@ -160,13 +160,13 @@ const router = new VueRouter({
|
|||
{
|
||||
path: "runs/:runid",
|
||||
name: "user project run",
|
||||
component: run,
|
||||
component: runsummary,
|
||||
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), runid: route.params.runid })
|
||||
},
|
||||
{
|
||||
path: "runs/:runid/tasks/:taskid",
|
||||
name: "user project run task",
|
||||
component: task,
|
||||
component: tasksummary,
|
||||
props: (route) => ({ ownertype: "user", ownername: route.params.username, projectref: parseRef(route.params.projectref), runid: route.params.runid, taskid: route.params.taskid })
|
||||
},
|
||||
{
|
||||
|
@ -298,13 +298,13 @@ const router = new VueRouter({
|
|||
{
|
||||
path: "runs/:runid",
|
||||
name: "org project run",
|
||||
component: run,
|
||||
component: runsummary,
|
||||
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), runid: route.params.runid })
|
||||
},
|
||||
{
|
||||
path: "runs/:runid/tasks/:taskid",
|
||||
name: "org project run task",
|
||||
component: task,
|
||||
component: tasksummary,
|
||||
props: (route) => ({ ownertype: "org", ownername: route.params.orgname, projectref: parseRef(route.params.projectref), runid: route.params.runid, taskid: route.params.taskid })
|
||||
},
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue