feat(series): browse search + per-card kebab (rename/delete)
CI / lint (push) Successful in 2s
CI / backend-lint-and-test (push) Successful in 26s
CI / frontend-build (push) Successful in 49s
CI / integration (push) Successful in 3m13s

The Series browse tab had no way to find a series in a long grid and no
per-series actions. Add a search field (instant client-side name/artist filter
over the already-loaded list) and a kebab on each card with Rename (reuses
TagRenameDialog → PATCH /api/tags/<id>, with its collision-merge flow) and
Delete (confirm dialog → DELETE /api/admin/tags/<id>; series_page/chapter/
suggestion cascade, images kept). Gap badge moved to the cover's top-left so the
kebab can sit top-right. 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 18:56:45 -04:00
parent 4958e8f7d4
commit e4cebf70d1
2 changed files with 146 additions and 5 deletions
+18 -1
View File
@@ -27,5 +27,22 @@ export const useSeriesBrowseStore = defineStore('seriesBrowse', () => {
return load()
}
return { series, sort, artistId, loading, error, load, setSort }
// A series IS a Tag(kind=series); deleting it removes the series structure
// (pages/chapters cascade) via the shared admin tag-delete. Splice the card
// out for instant feedback rather than reloading the whole grid.
async function remove(id) {
await api.delete(`/api/admin/tags/${id}`)
series.value = series.value.filter(s => s.id !== id)
}
// Reflect an in-place rename (PATCH /api/tags/<id>) without a full reload.
function applyRename(id, name) {
const row = series.value.find(s => s.id === id)
if (row) row.name = name
}
return {
series, sort, artistId, loading, error,
load, setSort, remove, applyRename,
}
})