diff --git a/client/components/modals/EditSeriesInputInnerModal.vue b/client/components/modals/EditSeriesInputInnerModal.vue index bd568321f..05c4dc2c1 100644 --- a/client/components/modals/EditSeriesInputInnerModal.vue +++ b/client/components/modals/EditSeriesInputInnerModal.vue @@ -8,7 +8,7 @@
- +
diff --git a/client/components/widgets/SeriesInputWidget.vue b/client/components/widgets/SeriesInputWidget.vue index 3dab0605a..afca93c19 100644 --- a/client/components/widgets/SeriesInputWidget.vue +++ b/client/components/widgets/SeriesInputWidget.vue @@ -19,6 +19,7 @@ export default { return { selectedSeries: null, originalSeriesSequence: null, + originalSeriesName: null, showSeriesForm: false } }, @@ -61,6 +62,7 @@ export default { } this.originalSeriesSequence = _series.sequence + this.originalSeriesName = _series.name this.showSeriesForm = true }, addNewSeries() { @@ -71,6 +73,7 @@ export default { } this.originalSeriesSequence = null + this.originalSeriesName = null this.showSeriesForm = true }, submitSeriesForm() { @@ -81,6 +84,18 @@ export default { var existingSeriesIndex = this.seriesItems.findIndex((se) => se.id === this.selectedSeries.id) + // Check if renaming to a name that already exists in the library (different series) + var seriesSameName = this.series.find((se) => se.name.toLowerCase() === this.selectedSeries.name.toLowerCase()) + if (seriesSameName && seriesSameName.id !== this.selectedSeries.id) { + // If editing an existing series and trying to rename to an existing name, block it + if (!this.selectedSeries.id.startsWith('new-')) { + this.$toast.error(this.$strings.ToastSeriesDuplicateName) + return + } + // For new series, use the existing series id instead + this.selectedSeries.id = seriesSameName.id + } + var existingSeriesSameName = this.seriesItems.findIndex((se) => se.name.toLowerCase() === this.selectedSeries.name.toLowerCase()) if (existingSeriesSameName >= 0 && existingSeriesIndex < 0) { console.error('Attempt to add duplicate series') @@ -88,11 +103,6 @@ export default { return } - var seriesSameName = this.series.find((se) => se.name.toLowerCase() === this.selectedSeries.name.toLowerCase()) - if (existingSeriesIndex < 0 && seriesSameName) { - this.selectedSeries.id = seriesSameName.id - } - var selectedSeriesCopy = { ...this.selectedSeries } selectedSeriesCopy.displayName = selectedSeriesCopy.sequence ? `${selectedSeriesCopy.name} #${selectedSeriesCopy.sequence}` : selectedSeriesCopy.name @@ -105,7 +115,25 @@ export default { this.seriesItems = seriesCopy } + // If this is an existing series (not new), update the series name immediately + if (!this.selectedSeries.id.startsWith('new-')) { + const hasNameChanged = this.originalSeriesName && this.selectedSeries.name !== this.originalSeriesName + if (hasNameChanged) { + this.updateSeriesName(this.selectedSeries.id, this.selectedSeries.name) + } + } + this.showSeriesForm = false + }, + async updateSeriesName(seriesId, name) { + try { + await this.$axios.$patch(`/api/series/${seriesId}`, { name }) + this.$toast.success(this.$strings.ToastSeriesUpdateSuccess) + } catch (error) { + console.error('Failed to update series name:', error) + const errorMsg = error.response?.data || this.$strings.ToastSeriesUpdateFailed + this.$toast.error(errorMsg) + } } } } diff --git a/client/strings/en-us.json b/client/strings/en-us.json index fb2bcb281..7ee588dc2 100644 --- a/client/strings/en-us.json +++ b/client/strings/en-us.json @@ -1127,6 +1127,7 @@ "ToastSelectAtLeastOneUser": "Select at least one user", "ToastSendEbookToDeviceFailed": "Failed to send ebook to device", "ToastSendEbookToDeviceSuccess": "Ebook sent to device \"{0}\"", + "ToastSeriesDuplicateName": "A series with that name already exists in this library", "ToastSeriesSubmitFailedSameName": "Cannot add two series with the same name", "ToastSeriesUpdateFailed": "Series update failed", "ToastSeriesUpdateSuccess": "Series update success", diff --git a/server/controllers/SeriesController.js b/server/controllers/SeriesController.js index 21c93f332..4c1357292 100644 --- a/server/controllers/SeriesController.js +++ b/server/controllers/SeriesController.js @@ -6,6 +6,7 @@ const Database = require('../Database') const RssFeedManager = require('../managers/RssFeedManager') const libraryItemsBookFilters = require('../utils/queries/libraryItemsBookFilters') +const { getTitleIgnorePrefix } = require('../utils/index') /** * @typedef RequestUserObject @@ -62,7 +63,8 @@ class SeriesController { } /** - * TODO: Currently unused in the client, should check for duplicate name + * PATCH /api/series/:id + * Update series metadata (name, description) * * @param {SeriesControllerRequest} req * @param {Response} res @@ -78,6 +80,17 @@ class SeriesController { if (!Object.keys(payload).length) { return res.status(400).send('No valid fields to update') } + + // Check for duplicate series name in the same library + if (payload.name && payload.name.toLowerCase() !== req.series.name.toLowerCase()) { + const existingSeries = await Database.seriesModel.getByNameAndLibrary(payload.name, req.series.libraryId) + if (existingSeries && existingSeries.id !== req.series.id) { + return res.status(400).send('A series with that name already exists in this library') + } + // Update nameIgnorePrefix when name changes + payload.nameIgnorePrefix = getTitleIgnorePrefix(payload.name) + } + req.series.set(payload) if (req.series.changed()) { await req.series.save()