feat(series): operator-set sparse page numbers + gap blocks (#789 tweak)
Replaces the auto-renumbered 1..N position key with operator-OWNED page numbers: sparse, gaps allowed, editable, never auto-renumbered. Order follows the numbers; unnumbered pages sort to the tail. This is the fix for the model that clobbered hand-set numbers on the flatten — numbers are now data, not a derived sequence. - series_service: drop the renumber-on-reorder/remove; order by page_number NULLS LAST; new set_page_number(image_id, n|None); list_pages returns `gaps` (one entry per missing-number run) + each pending group's parsed `start_page`; set_cover renumbers below the current min; place_pending(image_ids, start_page) numbers placed pages sequentially from the start (drop junk first → numbers line up); add_post stamps the parsed start on staged pages. - api/tags: POST /series/<id>/pages/number (set one page's number); /pending/ place takes start_page; removed /reorder. - frontend: per-card editable number input; one gap block per gap with drop-on-edge to assign the adjacent number (middle → type); append drop zone; pending tray gets a "from page N" field + "Place from page N". - tests reworked: sparse numbers + gaps, place-from-start, set-page-number route. No migration; nothing destructive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,19 +42,30 @@ describe('seriesManage', () => {
|
||||
expect(s.pageCount).toBe(1)
|
||||
})
|
||||
|
||||
it('reorder posts ordered ids to the series reorder route', async () => {
|
||||
it('setPageNumber posts the image + number to /pages/number', async () => {
|
||||
const s = useSeriesManageStore()
|
||||
s.tagId = 7
|
||||
const calls = []
|
||||
stubFetch((url, init) => {
|
||||
calls.push({ url, body: init.body ? JSON.parse(init.body) : null })
|
||||
if (url.includes('/reorder')) return { status: 200, body: { ok: true } }
|
||||
if (url.includes('/pages/number')) return { status: 200, body: { ok: true } }
|
||||
return { status: 200, body: SERIES_BODY }
|
||||
})
|
||||
await s.reorder([30, 10, 20])
|
||||
const r = calls.find(c => c.url.includes('/reorder'))
|
||||
expect(r.url).toContain('/api/series/7/reorder')
|
||||
expect(r.body).toEqual({ image_ids: [30, 10, 20] })
|
||||
await s.setPageNumber(42, 5)
|
||||
const r = calls.find(c => c.url.includes('/pages/number'))
|
||||
expect(r.url).toContain('/api/series/7/pages/number')
|
||||
expect(r.body).toEqual({ image_id: 42, page_number: 5 })
|
||||
})
|
||||
|
||||
it('gapAfter looks up a gap by its after_image_id', async () => {
|
||||
const s = useSeriesManageStore()
|
||||
stubFetch(() => ({
|
||||
status: 200,
|
||||
body: { ...SERIES_BODY, gaps: [{ after_image_id: 1, before_image_id: 9, from: 2, to: 3 }] }
|
||||
}))
|
||||
await s.load(7)
|
||||
expect(s.gapAfter(1)).toEqual({ after_image_id: 1, before_image_id: 9, from: 2, to: 3 })
|
||||
expect(s.gapAfter(99)).toBeNull()
|
||||
})
|
||||
|
||||
it('createDivider posts the anchor image + labels', async () => {
|
||||
|
||||
Reference in New Issue
Block a user