-
+
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()