feat(fc2b): wire SuggestionsPanel + rename into TagPanel; reject-on-remove
TagPanel now renders SuggestionsPanel below the current-tags area and adds a per-chip kebab → Rename… (TagRenameDialog). modal store's removeTag now also POSTs suggestions/dismiss after the delete so removing an auto-applied tag records a per-image rejection and the allowlist maintenance sweep won't re-apply it (closes the remove→re-apply loop identified in the spec). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,19 @@
|
||||
>
|
||||
<v-icon start size="x-small">{{ iconFor(tag.kind) }}</v-icon>
|
||||
{{ tag.name }}<span v-if="tag.fandom_id">→</span>
|
||||
<v-menu>
|
||||
<template #activator="{ props: mp }">
|
||||
<v-icon
|
||||
v-bind="mp" size="x-small" class="ml-1"
|
||||
icon="mdi-dots-vertical" @click.stop
|
||||
/>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item @click="openRename(tag)">
|
||||
<v-list-item-title>Rename…</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</v-chip>
|
||||
<span v-if="!modal.current?.tags?.length" class="text-caption">No tags yet.</span>
|
||||
</div>
|
||||
@@ -23,6 +36,19 @@
|
||||
<v-alert v-if="errorMsg" type="error" variant="tonal" class="mt-2" closable>
|
||||
{{ errorMsg }}
|
||||
</v-alert>
|
||||
|
||||
<SuggestionsPanel
|
||||
v-if="modal.currentImageId != null"
|
||||
:image-id="modal.currentImageId"
|
||||
/>
|
||||
|
||||
<v-dialog v-model="renameDialog" max-width="420">
|
||||
<TagRenameDialog
|
||||
v-if="renameTarget"
|
||||
:tag="renameTarget"
|
||||
@renamed="onRenamed" @cancel="renameDialog = false"
|
||||
/>
|
||||
</v-dialog>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
@@ -31,6 +57,8 @@ import { ref } from 'vue'
|
||||
import { useModalStore } from '../../stores/modal.js'
|
||||
import { useTagStore } from '../../stores/tags.js'
|
||||
import TagAutocomplete from './TagAutocomplete.vue'
|
||||
import SuggestionsPanel from './SuggestionsPanel.vue'
|
||||
import TagRenameDialog from './TagRenameDialog.vue'
|
||||
|
||||
const modal = useModalStore()
|
||||
const store = useTagStore()
|
||||
@@ -58,6 +86,19 @@ async function onPickNew(payload) {
|
||||
try { await modal.createAndAdd(payload) }
|
||||
catch (e) { errorMsg.value = e.message }
|
||||
}
|
||||
|
||||
const renameDialog = ref(false)
|
||||
const renameTarget = ref(null)
|
||||
|
||||
function openRename(tag) {
|
||||
renameTarget.value = tag
|
||||
renameDialog.value = true
|
||||
}
|
||||
async function onRenamed() {
|
||||
renameDialog.value = false
|
||||
// Reflect the new name in the modal's current tag list without a full reload.
|
||||
await modal.reloadTags()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -53,7 +53,13 @@ export const useModalStore = defineStore('modal', () => {
|
||||
const prev = current.value.tags
|
||||
current.value.tags = current.value.tags.filter(t => t.id !== tagId)
|
||||
try {
|
||||
// FC-2b: removal also records a per-image rejection (suggestions/dismiss
|
||||
// is the rejection-recording endpoint) so the allowlist maintenance
|
||||
// task won't re-apply this tag to this image.
|
||||
await api.delete(`/api/images/${currentImageId.value}/tags/${tagId}`)
|
||||
await api.post(`/api/images/${currentImageId.value}/suggestions/dismiss`, {
|
||||
body: { tag_id: tagId }
|
||||
})
|
||||
} catch (e) {
|
||||
current.value.tags = prev
|
||||
window.__fcToast?.({ text: `Failed to remove tag: ${e.message}`, type: 'error' })
|
||||
|
||||
Reference in New Issue
Block a user