feat(series): rename a series from the management view
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 21s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m15s

The management view showed the series name but had no way to change it post-
creation (rename was only on the browse-card kebab). Add a pencil next to the
title that opens TagRenameDialog (reuses the canonical rename → PATCH
/api/tags/<id> with its collision→merge flow, since a series IS a
Tag(kind=series)); the new name reflects in place. Operator-asked 2026-06-09.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-09 23:05:01 -04:00
parent 4e83b4225a
commit 409bbd43db
+23
View File
@@ -2,6 +2,12 @@
<v-container fluid class="pt-2 pb-10 fc-series"> <v-container fluid class="pt-2 pb-10 fc-series">
<div class="fc-series__head"> <div class="fc-series__head">
<span class="fc-series__name">{{ store.series?.name || 'Series' }}</span> <span class="fc-series__name">{{ store.series?.name || 'Series' }}</span>
<v-btn
v-if="store.series"
icon="mdi-pencil" size="x-small" variant="text"
:aria-label="`Rename ${store.series.name}`"
@click="renameOpen = true"
/>
<span class="fc-series__count"> <span class="fc-series__count">
{{ store.chapters.length }} part(s) · {{ store.pageCount }} page(s) {{ store.chapters.length }} part(s) · {{ store.pageCount }} page(s)
</span> </span>
@@ -214,6 +220,14 @@
<div ref="sentinel" class="fc-picker__sentinel" /> <div ref="sentinel" class="fc-picker__sentinel" />
</div> </div>
</v-navigation-drawer> </v-navigation-drawer>
<v-dialog v-model="renameOpen" max-width="420">
<TagRenameDialog
v-if="store.series"
:tag="{ id: store.tagId, name: store.series.name, kind: 'series' }"
@renamed="onRenamed" @cancel="renameOpen = false"
/>
</v-dialog>
</v-container> </v-container>
</template> </template>
@@ -223,6 +237,7 @@ import { useRoute } from 'vue-router'
import { useSeriesManageStore, moveItem } from '../stores/seriesManage.js' import { useSeriesManageStore, moveItem } from '../stores/seriesManage.js'
import { useInfiniteScroll } from '../composables/useInfiniteScroll.js' import { useInfiniteScroll } from '../composables/useInfiniteScroll.js'
import KebabMenu from '../components/common/KebabMenu.vue' import KebabMenu from '../components/common/KebabMenu.vue'
import TagRenameDialog from '../components/modal/TagRenameDialog.vue'
const route = useRoute() const route = useRoute()
const store = useSeriesManageStore() const store = useSeriesManageStore()
@@ -231,6 +246,14 @@ const drag = ref(null) // { chapterId, idx }
const titleDraft = reactive({}) // chapterId -> draft title const titleDraft = reactive({}) // chapterId -> draft title
const partDraft = reactive({}) // chapterId -> draft stated_part (string) const partDraft = reactive({}) // chapterId -> draft stated_part (string)
const pickerOpen = ref(false) const pickerOpen = ref(false)
const renameOpen = ref(false)
// A series IS a Tag(kind=series); TagRenameDialog PATCHes /api/tags/<id> and
// handles the same-name collision→merge flow. Reflect the new name in place.
function onRenamed(updated) {
renameOpen.value = false
if (store.series && updated?.name) store.series.name = updated.name
}
// Keep local drafts in sync with loaded chapters. Edits commit on blur/Enter, // Keep local drafts in sync with loaded chapters. Edits commit on blur/Enter,
// which refreshes and resets the draft to the saved value. // which refreshes and resets the draft to the saved value.