Web: also let Vue Router track the active task
This basically does the same as 63ac7287321a101c3f601eeb151be73154ef7720 but then for tasks.
This commit is contained in:
parent
6b9d7dba6d
commit
cc10d3e4bb
@ -28,7 +28,8 @@ export default {
|
||||
},
|
||||
|
||||
_handleTaskActionPromise(promise, description) {
|
||||
const numTasks = this.tasks.numSelected;
|
||||
// const numTasks = this.tasks.numSelected;
|
||||
const numTasks = 1;
|
||||
return promise
|
||||
.then(() => {
|
||||
let message;
|
||||
|
@ -16,9 +16,10 @@ import { useTasks } from '@/stores/tasks';
|
||||
import TaskActionsBar from '@/components/TaskActionsBar.vue'
|
||||
|
||||
export default {
|
||||
emits: ["selectedTaskChange"],
|
||||
emits: ["tableRowClicked"],
|
||||
props: [
|
||||
"jobID", // ID of the job of which the tasks are shown here.
|
||||
"taskID", // The active task.
|
||||
],
|
||||
components: {
|
||||
TaskActionsBar,
|
||||
@ -27,7 +28,11 @@ export default {
|
||||
const options = {
|
||||
// See pkg/api/flamenco-manager.yaml, schemas Task and TaskUpdate.
|
||||
columns: [
|
||||
{ formatter: "rowSelection", titleFormatter: "rowSelection", hozAlign: "center", headerHozAlign: "center", headerSort: false },
|
||||
// { formatter: "rowSelection", titleFormatter: "rowSelection", hozAlign: "center", headerHozAlign: "center", headerSort: false },
|
||||
{
|
||||
title: "ID", field: "id", headerSort: false,
|
||||
formatter: (cell) => cell.getData().id.substr(0, 8),
|
||||
},
|
||||
{
|
||||
title: 'Status', field: 'status', sorter: 'string',
|
||||
formatter(cell, formatterParams) { // eslint-disable-line no-unused-vars
|
||||
@ -53,7 +58,7 @@ export default {
|
||||
{ column: "updated", dir: "desc" },
|
||||
],
|
||||
data: [], // Will be filled via a Flamenco API request.
|
||||
selectable: 1, // Only allow a single row to be selected at a time.
|
||||
selectable: false, // The active task is tracked by click events.
|
||||
};
|
||||
return {
|
||||
options: options,
|
||||
@ -65,16 +70,28 @@ export default {
|
||||
// tasksTableVue.processTaskUpdate({id: "ad0a5a00-5cb8-4e31-860a-8a405e75910e", status: "heyy", updated: DateTime.local().toISO(), previous_status: "uuuuh", name: "Updated manually"});
|
||||
// tasksTableVue.processTaskUpdate({id: "ad0a5a00-5cb8-4e31-860a-8a405e75910e", status: "heyy", updated: DateTime.local().toISO()});
|
||||
window.tasksTableVue = this;
|
||||
|
||||
// Set the `rowFormatter` here (instead of with the rest of the options
|
||||
// above) as it needs to refer to `this`, which isn't available in the
|
||||
// `data` function.
|
||||
this.options.rowFormatter = (row) => {
|
||||
const data = row.getData();
|
||||
const isActive = (data.id === this.taskID);
|
||||
row.getElement().classList.toggle("active-row", isActive);
|
||||
};
|
||||
|
||||
this.tabulator = new Tabulator('#flamenco_task_list', this.options);
|
||||
this.tabulator.on("rowSelected", this.onRowSelected);
|
||||
this.tabulator.on("rowDeselected", this.onRowDeselected);
|
||||
this.tabulator.on("rowClick", this.onRowClick);
|
||||
this.tabulator.on("tableBuilt", this.fetchTasks);
|
||||
},
|
||||
watch: {
|
||||
jobID() {
|
||||
this.onRowDeselected([]);
|
||||
this.fetchTasks();
|
||||
},
|
||||
taskID(oldID, newID) {
|
||||
this._reformatRow(oldID);
|
||||
this._reformatRow(newID);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onReconnected() {
|
||||
@ -103,7 +120,6 @@ export default {
|
||||
// "Down-cast" to TaskUpdate to only get those fields, just for debugging things:
|
||||
// let tasks = data.tasks.map((j) => API.TaskUpdate.constructFromObject(j));
|
||||
this.tabulator.setData(data.tasks);
|
||||
this._restoreRowSelection();
|
||||
},
|
||||
processTaskUpdate(taskUpdate) {
|
||||
// updateData() will only overwrite properties that are actually set on
|
||||
@ -112,27 +128,22 @@ export default {
|
||||
.then(this.sortData);
|
||||
},
|
||||
|
||||
// Selection handling.
|
||||
onRowSelected(selectedRow) {
|
||||
const selectedData = selectedRow.getData();
|
||||
this._storeRowSelection([selectedData]);
|
||||
this.$emit("selectedTaskChange", selectedData);
|
||||
},
|
||||
onRowDeselected(deselectedRow) {
|
||||
this._storeRowSelection([]);
|
||||
this.$emit("selectedTaskChange", null);
|
||||
},
|
||||
_storeRowSelection(selectedData) {
|
||||
const selectedTaskIDs = selectedData.map((row) => row.id);
|
||||
localStorage.setItem("selectedTaskIDs", selectedTaskIDs);
|
||||
},
|
||||
_restoreRowSelection() {
|
||||
// const selectedTaskIDs = localStorage.getItem('selectedTaskIDs');
|
||||
// if (!selectedTaskIDs) {
|
||||
// return;
|
||||
// }
|
||||
// this.tabulator.selectRow(selectedTaskIDs);
|
||||
onRowClick(event, row) {
|
||||
// Take a copy of the data, so that it's decoupled from the tabulator data
|
||||
// store. There were some issues where navigating to another job would
|
||||
// overwrite the old job's ID, and this prevents that.
|
||||
const rowData = plain(row.getData());
|
||||
this.$emit("tableRowClicked", rowData);
|
||||
},
|
||||
|
||||
_reformatRow(jobID) {
|
||||
// Use tab.rowManager.findRow() instead of `tab.getRow()` as the latter
|
||||
// logs a warning when the row cannot be found.
|
||||
const row = this.tabulator.rowManager.findRow(jobID);
|
||||
if (!row) return
|
||||
if (row.reformat) row.reformat();
|
||||
else if (row.reinitialize) row.reinitialize(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -9,7 +9,7 @@ const router = createRouter({
|
||||
component: () => import('../views/IndexView.vue'),
|
||||
},
|
||||
{
|
||||
path: '/jobs/:jobID?',
|
||||
path: '/jobs/:jobID?/:taskID?',
|
||||
name: 'jobs',
|
||||
component: () => import('../views/JobsView.vue'),
|
||||
props: true,
|
||||
|
@ -10,8 +10,6 @@ const jobsAPI = new API.JobsApi(apiClient);
|
||||
// See https://pinia.vuejs.org/core-concepts/
|
||||
export const useTasks = defineStore('tasks', {
|
||||
state: () => ({
|
||||
/** @type {API.Task[]} */
|
||||
selectedTasks: [],
|
||||
/** @type {API.Task} */
|
||||
activeTask: null,
|
||||
/**
|
||||
@ -21,9 +19,6 @@ export const useTasks = defineStore('tasks', {
|
||||
activeTaskID: "",
|
||||
}),
|
||||
getters: {
|
||||
numSelected() {
|
||||
return this.selectedTasks.length;
|
||||
},
|
||||
canCancel() {
|
||||
return this._anyTaskWithStatus(["queued", "active", "soft-failed"])
|
||||
},
|
||||
@ -32,25 +27,20 @@ export const useTasks = defineStore('tasks', {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
// Selection of tasks.
|
||||
setSelectedTask(task) {
|
||||
setActiveTaskID(taskID) {
|
||||
this.$patch({
|
||||
activeTask: {id: taskID},
|
||||
activeTaskID: taskID,
|
||||
});
|
||||
},
|
||||
setActiveTask(task) {
|
||||
this.$patch({
|
||||
selectedTasks: [task],
|
||||
activeTask: task,
|
||||
activeTaskID: task.id,
|
||||
});
|
||||
},
|
||||
setSelectedTasks(tasks) {
|
||||
const activeTask =tasks[tasks.length-1]; // Last-selected is the active one.
|
||||
this.$patch({
|
||||
selectedTasks: tasks,
|
||||
activeTask: activeTask,
|
||||
activeTaskID: activeTask.id,
|
||||
});
|
||||
},
|
||||
deselectAllTasks() {
|
||||
this.$patch({
|
||||
selectedTasks: [],
|
||||
activeTask: null,
|
||||
activeTaskID: "",
|
||||
});
|
||||
@ -75,7 +65,8 @@ export const useTasks = defineStore('tasks', {
|
||||
* @returns bool indicating whether there is a selected task with any of the given statuses.
|
||||
*/
|
||||
_anyTaskWithStatus(statuses) {
|
||||
return this.selectedTasks.reduce((foundTask, task) => (foundTask || statuses.includes(task.status)), false);
|
||||
return !!this.activeTask && !!this.activeTask.status && statuses.includes(this.activeTask.status);
|
||||
// return this.selectedTasks.reduce((foundTask, task) => (foundTask || statuses.includes(task.status)), false);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -84,8 +75,12 @@ export const useTasks = defineStore('tasks', {
|
||||
* @returns a Promise for the API request.
|
||||
*/
|
||||
_setTaskStatus(newStatus) {
|
||||
if (!this.activeTaskID) {
|
||||
console.warn(`_setTaskStatus(${newStatus}) impossible, no active task ID`);
|
||||
return;
|
||||
}
|
||||
const statuschange = new API.TaskStatusChange(newStatus, "requested from web interface");
|
||||
return jobsAPI.setTaskStatus(this.activeTask.id, statuschange);
|
||||
return jobsAPI.setTaskStatus(this.activeTaskID, statuschange);
|
||||
},
|
||||
},
|
||||
})
|
||||
|
@ -4,7 +4,7 @@
|
||||
</div>
|
||||
<div class="col col-2">
|
||||
<job-details :jobData="jobs.activeJob" />
|
||||
<tasks-table v-if="jobID" ref="tasksTable" :jobID="jobID" @selectedTaskChange="onSelectedTaskChanged" />
|
||||
<tasks-table v-if="jobID" ref="tasksTable" :jobID="jobID" :taskID="taskID" @tableRowClicked="onTableTaskClicked" />
|
||||
</div>
|
||||
<div class="col col-3">
|
||||
<task-details :taskData="tasks.activeTask" />
|
||||
@ -33,7 +33,7 @@ import UpdateListener from '@/components/UpdateListener.vue'
|
||||
|
||||
export default {
|
||||
name: 'JobsView',
|
||||
props: ["jobID"], // provided by Vue Router.
|
||||
props: ["jobID", "taskID"], // provided by Vue Router.
|
||||
components: {
|
||||
JobsTable, JobDetails, TaskDetails, TasksTable, NotificationBar, UpdateListener,
|
||||
},
|
||||
@ -47,19 +47,26 @@ export default {
|
||||
mounted() {
|
||||
window.jobsView = this;
|
||||
this._fetchJob(this.jobID);
|
||||
this._fetchTask(this.taskID);
|
||||
},
|
||||
watch: {
|
||||
jobID(newJobID, oldJobID) {
|
||||
this._fetchJob(newJobID);
|
||||
},
|
||||
taskID(newTaskID, oldTaskID) {
|
||||
this._fetchTask(newTaskID);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onTableJobClicked(rowData) {
|
||||
this._routeToJob(rowData.id);
|
||||
},
|
||||
onTableTaskClicked(rowData) {
|
||||
this._routeToTask(rowData.id);
|
||||
},
|
||||
|
||||
onSelectedTaskChanged(taskSummary) {
|
||||
if (!taskSummary) { // There is no selected task.
|
||||
if (!taskSummary) { // There is no active task.
|
||||
this.tasks.deselectAllTasks();
|
||||
return;
|
||||
}
|
||||
@ -67,7 +74,7 @@ export default {
|
||||
const jobsAPI = new API.JobsApi(apiClient);
|
||||
jobsAPI.fetchTask(taskSummary.id)
|
||||
.then((task) => {
|
||||
this.tasks.setSelectedTask(task);
|
||||
this.tasks.setActiveTask(task);
|
||||
// Forward the full task to Tabulator, so that that gets updated too.
|
||||
if (this.$refs.tasksTable)
|
||||
this.$refs.tasksTable.processTaskUpdate(task);
|
||||
@ -86,13 +93,33 @@ export default {
|
||||
this._fetchJob(this.jobID);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for SocketIO task updates.
|
||||
* @param {API.SocketIOTaskUpdate} taskUpdate
|
||||
*/
|
||||
onSioTaskUpdate(taskUpdate) {
|
||||
if (this.$refs.tasksTable)
|
||||
this.$refs.tasksTable.processTaskUpdate(taskUpdate);
|
||||
if (this.taskID == taskUpdate.id)
|
||||
this._fetchTask(this.taskID);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {string} jobID job ID to navigate to, can be empty string for "no active job".
|
||||
*/
|
||||
_routeToJob(jobID) {
|
||||
this.$router.push({ name: 'jobs', params: { jobID: jobID } });
|
||||
const route = { name: 'jobs', params: { jobID: jobID } };
|
||||
console.log("routing to job", route.params);
|
||||
this.$router.push(route);
|
||||
},
|
||||
/**
|
||||
* @param {string} taskID task ID to navigate to within this job, can be
|
||||
* empty string for "no active task".
|
||||
*/
|
||||
_routeToTask(taskID) {
|
||||
const route = { name: 'jobs', params: { jobID: this.jobID, taskID: taskID } };
|
||||
console.log("routing to task", route.params);
|
||||
this.$router.push(route);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -115,14 +142,22 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for SocketIO task updates.
|
||||
* @param {API.SocketIOTaskUpdate} taskUpdate
|
||||
* Fetch task info and set the active task once it's received.
|
||||
* @param {string} taskID task ID, can be empty string for "no task".
|
||||
*/
|
||||
onSioTaskUpdate(taskUpdate) {
|
||||
if (this.$refs.tasksTable)
|
||||
this.$refs.tasksTable.processTaskUpdate(taskUpdate);
|
||||
if (this.tasks.activeTaskID == taskUpdate.id)
|
||||
this.onSelectedTaskChanged(taskUpdate);
|
||||
_fetchTask(taskID) {
|
||||
if (!taskID) {
|
||||
this.tasks.deselectAllTasks();
|
||||
return;
|
||||
}
|
||||
|
||||
const jobsAPI = new API.JobsApi(apiClient);
|
||||
return jobsAPI.fetchTask(taskID)
|
||||
.then((task) => {
|
||||
this.tasks.setActiveTask(task);
|
||||
// Forward the full task to Tabulator, so that that gets updated too.
|
||||
this.$refs.tasksTable.processTaskUpdate(task);
|
||||
});
|
||||
},
|
||||
|
||||
onChatMessage(message) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user