fix(tags): rename-onto-existing in the image modal now merges, not errors
The image-modal tag kebab's rename dialog still showed a leftover stub
('Merging two tags into one lands in FC-2c') on a name collision, dead-ending
the operator. The merge machinery has existed for a while — the Tags view
already resolves rename collisions this way. Wire TagRenameDialog to it: on the
409 collision hint, show the same merge confirmation FandomSetDialog uses
(target name, image associations moved, alias kept) and POST /api/tags/<id>/merge
into the existing tag.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,25 +2,55 @@
|
||||
<v-card>
|
||||
<v-card-title>Rename tag</v-card-title>
|
||||
<v-card-text>
|
||||
<v-text-field
|
||||
v-model="newName" label="New name" density="compact"
|
||||
autofocus @keydown.enter="submit"
|
||||
/>
|
||||
<v-alert v-if="errorMsg" type="error" variant="tonal" density="compact" class="mt-2">
|
||||
{{ errorMsg }}
|
||||
<div v-if="isCollision" class="text-caption mt-1">
|
||||
Merging two tags into one lands in FC-2c.
|
||||
</div>
|
||||
</v-alert>
|
||||
<template v-if="!collision">
|
||||
<v-text-field
|
||||
v-model="newName" label="New name" density="compact"
|
||||
autofocus @keydown.enter="submit"
|
||||
/>
|
||||
<v-alert
|
||||
v-if="errorMsg" type="error" variant="tonal" density="compact"
|
||||
class="mt-2"
|
||||
>{{ errorMsg }}</v-alert>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<!-- A tag of this (kind, fandom) already has that name. Renaming onto it
|
||||
is a merge, not a fork — same resolution the Tags view offers. -->
|
||||
<v-alert type="warning" variant="tonal" density="compact" class="mb-3">
|
||||
A {{ tag.kind }} tag named “{{ collision.target.name }}” already exists.
|
||||
</v-alert>
|
||||
<p class="text-body-2">
|
||||
Merge “{{ tag.name }}” into “{{ collision.target.name }}”?
|
||||
{{ collision.source_image_count }} image
|
||||
association{{ collision.source_image_count === 1 ? '' : 's' }}
|
||||
will move over and this tag will be deleted{{
|
||||
collision.will_alias ? ' (its name kept as a tagger alias)' : '' }}.
|
||||
</p>
|
||||
<v-alert
|
||||
v-if="errorMsg" type="error" variant="tonal" density="compact"
|
||||
class="mt-3"
|
||||
>{{ errorMsg }}</v-alert>
|
||||
</template>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer />
|
||||
<v-btn @click="$emit('cancel')">Cancel</v-btn>
|
||||
<v-btn
|
||||
color="primary" rounded="pill"
|
||||
:disabled="!newName.trim() || newName === tag.name"
|
||||
:loading="busy" @click="submit"
|
||||
>Rename</v-btn>
|
||||
<template v-if="!collision">
|
||||
<v-btn :disabled="busy" @click="$emit('cancel')">Cancel</v-btn>
|
||||
<v-btn
|
||||
color="primary" rounded="pill"
|
||||
:disabled="!newName.trim() || newName === tag.name"
|
||||
:loading="busy" @click="submit"
|
||||
>Rename</v-btn>
|
||||
</template>
|
||||
<template v-else>
|
||||
<v-btn :disabled="busy" @click="collision = null; errorMsg = null">
|
||||
Back
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="warning" variant="flat" rounded="pill"
|
||||
:loading="busy" @click="onMerge"
|
||||
>Merge</v-btn>
|
||||
</template>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -35,22 +65,41 @@ const emit = defineEmits(['renamed', 'cancel'])
|
||||
const api = useApi()
|
||||
const newName = ref(props.tag.name)
|
||||
const errorMsg = ref(null)
|
||||
const isCollision = ref(false)
|
||||
const collision = ref(null)
|
||||
const busy = ref(false)
|
||||
|
||||
async function submit() {
|
||||
if (!newName.value.trim() || newName.value === props.tag.name) return
|
||||
busy.value = true
|
||||
errorMsg.value = null
|
||||
isCollision.value = false
|
||||
try {
|
||||
const updated = await api.patch(`/api/tags/${props.tag.id}`, {
|
||||
body: { name: newName.value.trim() }
|
||||
})
|
||||
emit('renamed', updated)
|
||||
} catch (e) {
|
||||
// 409 → the new name collides with an existing tag of the same
|
||||
// (kind, fandom). Offer to merge into it rather than dead-ending.
|
||||
if (e.status === 409 && e.body && e.body.target) {
|
||||
collision.value = e.body
|
||||
} else {
|
||||
errorMsg.value = e.message
|
||||
}
|
||||
} finally {
|
||||
busy.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function onMerge() {
|
||||
busy.value = true
|
||||
errorMsg.value = null
|
||||
try {
|
||||
await api.post(`/api/tags/${props.tag.id}/merge`, {
|
||||
body: { target_id: collision.value.target.id }
|
||||
})
|
||||
emit('renamed', { id: collision.value.target.id, name: collision.value.target.name })
|
||||
} catch (e) {
|
||||
errorMsg.value = e.message
|
||||
isCollision.value = e.status === 409
|
||||
} finally {
|
||||
busy.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user