Skip to content

Commit 62c6e9c

Browse files
committed
feat: add tag removal from document slide-over drawer
TagBadge gains an optional removable prop with a dismiss (x) icon. DocumentSlideOver passes removable and emits removeTag. DocumentList handles the API call to strip the tag and refresh state.
1 parent 73278d9 commit 62c6e9c

3 files changed

Lines changed: 42 additions & 2 deletions

File tree

docs-web/src/main/webapp/src/components/DocumentSlideOver.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const props = defineProps<{
2222
const emit = defineEmits<{
2323
'update:visible': [value: boolean]
2424
addTag: [tagId: string]
25+
removeTag: [tagId: string]
2526
openFullView: []
2627
editDocument: [id: string]
2728
}>()
@@ -81,7 +82,7 @@ const tagOptions = computed(() =>
8182

8283
<div class="slide-section">
8384
<div class="slide-tags-row">
84-
<TagBadge v-for="tag in document.tags" :key="tag.id" :name="tag.name" :color="tag.color" />
85+
<TagBadge v-for="tag in document.tags" :key="tag.id" :name="tag.name" :color="tag.color" removable @remove="emit('removeTag', tag.id)" />
8586
<Button v-if="!slideOverTagAdding" icon="pi pi-plus" text rounded size="small" class="tag-add-btn" @click="slideOverTagAdding = true" aria-label="Add tag" />
8687
</div>
8788
<div v-if="slideOverTagAdding" class="tag-add-row">

docs-web/src/main/webapp/src/components/TagBadge.vue

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,29 @@
22
defineProps<{
33
name: string
44
color: string
5+
removable?: boolean
6+
}>()
7+
8+
const emit = defineEmits<{
9+
remove: []
510
}>()
611
</script>
712

813
<template>
9-
<span class="teedy-tag" :style="{ backgroundColor: color }">{{ name }}</span>
14+
<span class="teedy-tag" :style="{ backgroundColor: color }">{{ name }}<i v-if="removable" class="pi pi-times tag-remove-icon" role="button" aria-label="Remove tag" @click.stop="emit('remove')" /></span>
1015
</template>
16+
17+
<style scoped>
18+
.tag-remove-icon {
19+
font-size: 0.625rem;
20+
margin-left: 0.25rem;
21+
opacity: 0.7;
22+
cursor: pointer;
23+
border-radius: 50%;
24+
transition: opacity 0.15s;
25+
color: inherit;
26+
}
27+
.tag-remove-icon:hover {
28+
opacity: 1;
29+
}
30+
</style>

docs-web/src/main/webapp/src/views/document/DocumentList.vue

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,24 @@ async function addTagToSlideOver(tagId: string) {
138138
}
139139
}
140140
141+
async function removeTagFromSlideOver(tagId: string) {
142+
if (!slideOverDoc.value || !tagId) return
143+
const doc = slideOverDoc.value
144+
const currentTagIds = doc.tags?.map((t) => t.id).filter((id) => id !== tagId) ?? []
145+
const params = new URLSearchParams()
146+
params.set('title', doc.title)
147+
params.set('language', doc.language)
148+
for (const id of currentTagIds) params.append('tags', id)
149+
try {
150+
await updateDocument(doc.id, params)
151+
const { data } = await getDocument(doc.id)
152+
slideOverDoc.value = data
153+
queryClient.invalidateQueries({ queryKey: ['documents'] })
154+
} catch {
155+
showTagUpdateError()
156+
}
157+
}
158+
141159
function buildFilterLabel(): string {
142160
const parts: string[] = []
143161
for (const tag of tf.selectedTags) parts.push(tag.name)
@@ -244,6 +262,7 @@ const contextMenuItems = computed(() => {
244262
:document="slideOverDoc"
245263
:available-tags="availableTagsForSlideOver"
246264
@add-tag="addTagToSlideOver"
265+
@remove-tag="removeTagFromSlideOver"
247266
@open-full-view="openFullView"
248267
@edit-document="(id: string) => router.push({ name: 'document-edit', params: { id } })"
249268
/>

0 commit comments

Comments
 (0)