Web Interface for Tags #104244
164
web/app/src/views/TagsView.vue
Normal file
164
web/app/src/views/TagsView.vue
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<template>
|
||||||
|
<div class="col col-workers-list">
|
||||||
|
<h2 class="column-title">Tag Details</h2>
|
||||||
|
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button @click="createTag">Create Tag</button>
|
||||||
|
<button @click="deleteTag" :disabled="tags.length === 0">
|
||||||
|
Delete Tag
|
||||||
|
|||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Table to display tags -->
|
||||||
|
<table v-if="tags.length > 0" class="tag-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="tag in tags"
|
||||||
|
:key="tag.name"
|
||||||
|
@click="onTagClick(tag)"
|
||||||
|
:class="{ selected: isSelected(tag) }"
|
||||||
|
>
|
||||||
|
<td>{{ tag.name }}</td>
|
||||||
|
<td>{{ tag.description }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- Show message when no tags found -->
|
||||||
|
<div v-else class="dl-no-data">
|
||||||
|
<span>No tags found.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<footer class="app-footer"></footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.action-buttons {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons button {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add some basic styling to the table */
|
||||||
|
.tag-table {
|
||||||
|
background-color: var(--table-color-background-row-odd);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
font-family: var(--font-family-mono);
|
||||||
|
font-size: var(--font-size-sm);
|
||||||
|
text-align: left;
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
text-align: left;
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-table th {
|
||||||
|
height: 24px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-table td {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-table th {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-table tbody tr:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for selected row */
|
||||||
|
.tag-table tbody tr.selected {
|
||||||
|
background-color: rgb(137, 130, 201);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useWorkers } from "@/stores/workers";
|
||||||
|
import { useNotifs } from "@/stores/notifications";
|
||||||
|
import TabItem from "@/components/TabItem.vue";
|
||||||
|
import TabsWrapper from "@/components/TabsWrapper.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
TabItem,
|
||||||
|
TabsWrapper,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tags: [],
|
||||||
|
selectedTag: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.fetchTags();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetchTags() {
|
||||||
|
useWorkers()
|
||||||
|
.refreshTags()
|
||||||
|
.then(() => {
|
||||||
|
this.tags = useWorkers().tags;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorMsg = JSON.stringify(error);
|
||||||
|
useNotifs().add(`Error: ${errorMsg}`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
createTag() {
|
||||||
|
const newTag = { name: "name", description: "this is a test field" };
|
||||||
|
this.tags.push(newTag);
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteTag() {
|
||||||
|
if (this.tags.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.selectedTag) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Find the index of the selected tag in the tags array
|
||||||
|
const index = this.tags.findIndex((tag) => tag === this.selectedTag);
|
||||||
|
if (index !== -1) {
|
||||||
|
this.tags.splice(index, 1);
|
||||||
|
this.selectedTag = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onTagClick(tag) {
|
||||||
|
this.selectedTag = tag;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isSelected() {
|
||||||
|
return (tag) => tag === this.selectedTag;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user
For a followup PR, I think it would be nicer to remove this button, and have a little ❌ icon behind each tag (just like in
Blocklist.vue
for removing blocklist entries).Since selection of tags is only used for deleting them, this little change would remove the entire need to select tags. It also clears up the double semantics of clicking on a tag name: it currently both selects the tag and allows renaming it.