Web Interface for Tags (#104244)
Add a web interface for CRUD operations of worker tags. A 'Tags' tab is added to the web interface, with a list of available worker tags. New ones can be created, existing tags removed, and the name & description of tags can be edited. Reviewed-on: https://projects.blender.org/studio/flamenco/pulls/104244
This commit is contained in:
parent
60fb20c0ff
commit
c68a72ca49
@ -9,6 +9,9 @@
|
|||||||
<li>
|
<li>
|
||||||
<router-link :to="{ name: 'workers' }">Workers</router-link>
|
<router-link :to="{ name: 'workers' }">Workers</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<router-link :to="{ name: 'tags' }">Tags</router-link>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<router-link :to="{ name: 'last-rendered' }">Last Rendered</router-link>
|
<router-link :to="{ name: 'last-rendered' }">Last Rendered</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
@ -20,6 +20,12 @@ const router = createRouter({
|
|||||||
component: () => import('../views/WorkersView.vue'),
|
component: () => import('../views/WorkersView.vue'),
|
||||||
props: true,
|
props: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/tags',
|
||||||
|
name: 'tags',
|
||||||
|
component: () => import('../views/TagsView.vue'),
|
||||||
|
props: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/last-rendered',
|
path: '/last-rendered',
|
||||||
name: 'last-rendered',
|
name: 'last-rendered',
|
||||||
|
199
web/app/src/views/TagsView.vue
Normal file
199
web/app/src/views/TagsView.vue
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
<template>
|
||||||
|
<div class="col col-workers-list">
|
||||||
|
<h2 class="column-title">Tag Details</h2>
|
||||||
|
|
||||||
|
<div class="action-buttons btn-bar-group">
|
||||||
|
<div class="btn-bar">
|
||||||
|
<button @click="fetchTags">Refresh</button>
|
||||||
|
<button @click="deleteTag" :disabled="!selectedTag">Delete Tag</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="action-buttons btn-bar">
|
||||||
|
<form @submit="createTag">
|
||||||
|
<div class="create-tag-container">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="newtagname"
|
||||||
|
v-model="newTagName"
|
||||||
|
placeholder="New Tag Name"
|
||||||
|
class="create-tag-input"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
id="submit-button"
|
||||||
|
type="submit"
|
||||||
|
:disabled="newTagName.trim() === ''"
|
||||||
|
>
|
||||||
|
Create Tag
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tag-table-container"></div>
|
||||||
|
</div>
|
||||||
|
<footer class="app-footer"></footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.create-tag-container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.create-tag-input {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 10px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { TabulatorFull as Tabulator } from "tabulator-tables";
|
||||||
|
import { useWorkers } from "@/stores/workers";
|
||||||
|
import { useNotifs } from "@/stores/notifications";
|
||||||
|
import { WorkerMgtApi } from "@/manager-api";
|
||||||
|
import { WorkerTag } from "@/manager-api";
|
||||||
|
import { getAPIClient } from "@/api-client";
|
||||||
|
import TabItem from "@/components/TabItem.vue";
|
||||||
|
import TabsWrapper from "@/components/TabsWrapper.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
TabItem,
|
||||||
|
TabsWrapper,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tags: [],
|
||||||
|
selectedTag: null,
|
||||||
|
newTagName: "",
|
||||||
|
workers: useWorkers(),
|
||||||
|
activeRowIndex: -1,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.fetchTags();
|
||||||
|
|
||||||
|
const tag_options = {
|
||||||
|
columns: [
|
||||||
|
{ title: "Name", field: "name", sorter: "string", editor: "input" },
|
||||||
|
{
|
||||||
|
title: "Description",
|
||||||
|
field: "description",
|
||||||
|
sorter: "string",
|
||||||
|
editor: "input",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
layout: "fitData",
|
||||||
|
layoutColumnsOnNewData: true,
|
||||||
|
height: "82%",
|
||||||
|
selectable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.tabulator = new Tabulator("#tag-table-container", tag_options);
|
||||||
|
this.tabulator.on("rowClick", this.onRowClick);
|
||||||
|
this.tabulator.on("tableBuilt", () => {
|
||||||
|
this.fetchTags();
|
||||||
|
});
|
||||||
|
this.tabulator.on("cellEdited", (cell) => {
|
||||||
|
const editedTag = cell.getRow().getData();
|
||||||
|
this.updateTagInAPI(editedTag);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
_onTableBuilt() {
|
||||||
|
this.fetchTags();
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchTags() {
|
||||||
|
this.workers
|
||||||
|
.refreshTags()
|
||||||
|
.then(() => {
|
||||||
|
this.tags = this.workers.tags;
|
||||||
|
this.tabulator.setData(this.tags);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorMsg = JSON.stringify(error);
|
||||||
|
useNotifs().add(`Error: ${errorMsg}`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
createTag(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const api = new WorkerMgtApi(getAPIClient());
|
||||||
|
const newTag = new WorkerTag(this.newTagName);
|
||||||
|
|
||||||
|
newTag.description = "Default Description...";
|
||||||
|
|
||||||
|
api
|
||||||
|
.createWorkerTag(newTag)
|
||||||
|
.then(() => {
|
||||||
|
this.fetchTags(); // Refresh table data
|
||||||
|
this.newTagName = "";
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorMsg = JSON.stringify(error);
|
||||||
|
useNotifs().add(`Error: ${errorMsg}`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTagInAPI(tag) {
|
||||||
|
const { id: tagId, ...updatedTagData } = tag;
|
||||||
|
const api = new WorkerMgtApi(getAPIClient());
|
||||||
|
|
||||||
|
api
|
||||||
|
.updateWorkerTag(tagId, updatedTagData)
|
||||||
|
.then(() => {
|
||||||
|
// Update the local state with the edited data without requiring a page refresh
|
||||||
|
this.tags = this.tags.map((tag) => {
|
||||||
|
if (tag.id === tagId) {
|
||||||
|
return { ...tag, ...updatedTagData };
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
});
|
||||||
|
console.log("Tag updated successfully");
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorMsg = JSON.stringify(error);
|
||||||
|
useNotifs().add(`Error: ${errorMsg}`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteTag() {
|
||||||
|
if (!this.selectedTag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const api = new WorkerMgtApi(getAPIClient());
|
||||||
|
api
|
||||||
|
.deleteWorkerTag(this.selectedTag.id)
|
||||||
|
.then(() => {
|
||||||
|
this.selectedTag = null;
|
||||||
|
this.tabulator.setData(this.tags);
|
||||||
|
|
||||||
|
this.fetchTags();
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorMsg = JSON.stringify(error);
|
||||||
|
useNotifs().add(`Error: ${errorMsg}`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onRowClick(event, row) {
|
||||||
|
const tag = row.getData();
|
||||||
|
const rowIndex = row.getIndex();
|
||||||
|
|
||||||
|
this.tabulator.deselectRow();
|
||||||
|
this.tabulator.selectRow(rowIndex);
|
||||||
|
|
||||||
|
this.selectedTag = tag;
|
||||||
|
this.activeRowIndex = rowIndex;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
x
Reference in New Issue
Block a user