feat(sort-order): derive story/task sort_order from parseCodeNumber(code)
All create paths (createStoryAction, saveTask, createTaskAction, materializeIdeaPlanAction) and code-edit paths (updateStoryAction, saveTask update) now set sort_order = parseCodeNumber(code) instead of last+1. Removes stale last-record queries from create paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5085fac31d
commit
fec03dd257
3 changed files with 14 additions and 28 deletions
|
|
@ -21,6 +21,7 @@ import { canTransition, isGrillMdEditable, isIdeaEditable, isPlanMdEditable } fr
|
|||
import { nextIdeaCode } from '@/lib/idea-code-server'
|
||||
import { parsePlanMd } from '@/lib/idea-plan-parser'
|
||||
import { ACTIVE_JOB_STATUSES } from '@/lib/job-status'
|
||||
import { parseCodeNumber } from '@/lib/code'
|
||||
|
||||
import type { ClaudeJobKind, Idea, IdeaStatus } from '@prisma/client'
|
||||
|
||||
|
|
@ -720,16 +721,17 @@ export async function materializeIdeaPlanAction(
|
|||
|
||||
for (let si = 0; si < plan.stories.length; si++) {
|
||||
const s = plan.stories[si]
|
||||
const storyCode = `ST-${String(nextStoryN++).padStart(3, '0')}`
|
||||
const story = await tx.story.create({
|
||||
data: {
|
||||
pbi_id: pbi.id,
|
||||
product_id: productId,
|
||||
code: `ST-${String(nextStoryN++).padStart(3, '0')}`,
|
||||
code: storyCode,
|
||||
title: s.title,
|
||||
description: s.description ?? null,
|
||||
acceptance_criteria: s.acceptance_criteria ?? null,
|
||||
priority: s.priority,
|
||||
sort_order: si + 1, // sequential within PBI
|
||||
sort_order: parseCodeNumber(storyCode),
|
||||
status: 'OPEN',
|
||||
},
|
||||
select: { id: true },
|
||||
|
|
@ -738,11 +740,12 @@ export async function materializeIdeaPlanAction(
|
|||
|
||||
for (let ti = 0; ti < s.tasks.length; ti++) {
|
||||
const t = s.tasks[ti]
|
||||
const taskCode = `T-${nextTaskN++}`
|
||||
const task = await tx.task.create({
|
||||
data: {
|
||||
story_id: story.id,
|
||||
product_id: productId,
|
||||
code: `T-${nextTaskN++}`,
|
||||
code: taskCode,
|
||||
title: t.title,
|
||||
description: t.description ?? null,
|
||||
implementation_plan: t.implementation_plan ?? null,
|
||||
|
|
@ -751,7 +754,7 @@ export async function materializeIdeaPlanAction(
|
|||
// gemixte task-priorities binnen één story zouden anders de
|
||||
// YAML-volgorde verstoren (zie plan-fix task-volgorde-na-upload).
|
||||
priority: s.priority,
|
||||
sort_order: ti + 1,
|
||||
sort_order: parseCodeNumber(taskCode),
|
||||
status: 'TO_DO',
|
||||
verify_required: t.verify_required ?? 'ALIGNED_OR_PARTIAL',
|
||||
verify_only: t.verify_only ?? false,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { prisma } from '@/lib/prisma'
|
|||
import { SessionData, sessionOptions } from '@/lib/session'
|
||||
import { getAccessibleProduct, productAccessFilter } from '@/lib/product-access'
|
||||
import { requireProductWriter } from '@/lib/auth'
|
||||
import { isValidCode, normalizeCode } from '@/lib/code'
|
||||
import { isValidCode, normalizeCode, parseCodeNumber } from '@/lib/code'
|
||||
import { createWithCodeRetry, generateNextStoryCode } from '@/lib/code-server'
|
||||
import { createStorySchema, updateStorySchema } from '@/lib/schemas/story'
|
||||
import { enforceUserRateLimit } from '@/lib/rate-limit'
|
||||
|
|
@ -78,12 +78,6 @@ export async function createStoryAction(_prevState: unknown, formData: FormData)
|
|||
}
|
||||
}
|
||||
|
||||
const last = await prisma.story.findFirst({
|
||||
where: { pbi_id: parsed.data.pbiId, priority: parsed.data.priority },
|
||||
orderBy: { sort_order: 'desc' },
|
||||
})
|
||||
const sort_order = (last?.sort_order ?? 0) + 1.0
|
||||
|
||||
const insert = (code: string) =>
|
||||
prisma.story.create({
|
||||
data: {
|
||||
|
|
@ -94,7 +88,7 @@ export async function createStoryAction(_prevState: unknown, formData: FormData)
|
|||
description: parsed.data.description ?? null,
|
||||
acceptance_criteria: parsed.data.acceptance_criteria ?? null,
|
||||
priority: parsed.data.priority,
|
||||
sort_order,
|
||||
sort_order: parseCodeNumber(code),
|
||||
status: 'OPEN',
|
||||
},
|
||||
})
|
||||
|
|
@ -167,7 +161,7 @@ export async function updateStoryAction(_prevState: unknown, formData: FormData)
|
|||
await prisma.story.update({
|
||||
where: { id: parsed.data.id },
|
||||
data: {
|
||||
...(code ? { code } : {}),
|
||||
...(code ? { code, sort_order: parseCodeNumber(code) } : {}),
|
||||
title: parsed.data.title,
|
||||
description: parsed.data.description ?? null,
|
||||
acceptance_criteria: parsed.data.acceptance_criteria ?? null,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { productAccessFilter } from '@/lib/product-access'
|
|||
import { requireProductWriter } from '@/lib/auth'
|
||||
import { taskSchema as sharedTaskSchema, type TaskInput } from '@/lib/schemas/task'
|
||||
import { propagateStatusUpwards } from '@/lib/tasks-status-update'
|
||||
import { normalizeCode } from '@/lib/code'
|
||||
import { normalizeCode, parseCodeNumber } from '@/lib/code'
|
||||
import { createWithCodeRetry, generateNextTaskCode, isCodeUniqueConflict } from '@/lib/code-server'
|
||||
import { enforceUserRateLimit } from '@/lib/rate-limit'
|
||||
|
||||
|
|
@ -80,6 +80,7 @@ export async function saveTask(
|
|||
description: description ?? null,
|
||||
implementation_plan: implementation_plan ?? null,
|
||||
priority,
|
||||
...(inputCode ? { code: inputCode, sort_order: parseCodeNumber(inputCode) } : {}),
|
||||
},
|
||||
select: { id: true, title: true, status: true },
|
||||
})
|
||||
|
|
@ -106,15 +107,8 @@ export async function saveTask(
|
|||
})
|
||||
if (!story) return { ok: false, code: 403, error: 'forbidden' }
|
||||
|
||||
const last = await prisma.task.findFirst({
|
||||
where: { story_id: context.storyId },
|
||||
orderBy: { sort_order: 'desc' },
|
||||
select: { sort_order: true },
|
||||
})
|
||||
|
||||
const productId = story.product_id
|
||||
const sprintId = story.sprint_id ?? null
|
||||
const sortOrder = (last?.sort_order ?? 0) + 1.0
|
||||
const storyId = context.storyId
|
||||
|
||||
const task = await createWithCodeRetry(
|
||||
|
|
@ -130,7 +124,7 @@ export async function saveTask(
|
|||
description: description ?? null,
|
||||
implementation_plan: implementation_plan ?? null,
|
||||
priority,
|
||||
sort_order: sortOrder,
|
||||
sort_order: parseCodeNumber(code),
|
||||
status: 'TO_DO',
|
||||
},
|
||||
select: { id: true, title: true, status: true },
|
||||
|
|
@ -207,11 +201,6 @@ export async function createTaskAction(_prevState: unknown, formData: FormData)
|
|||
})
|
||||
if (!story) return { error: 'Story niet gevonden' }
|
||||
|
||||
const last = await prisma.task.findFirst({
|
||||
where: { story_id: storyId },
|
||||
orderBy: { sort_order: 'desc' },
|
||||
})
|
||||
|
||||
const productId = story.product_id
|
||||
const task = await createWithCodeRetry(
|
||||
() => generateNextTaskCode(productId),
|
||||
|
|
@ -225,7 +214,7 @@ export async function createTaskAction(_prevState: unknown, formData: FormData)
|
|||
title: parsed.data.title,
|
||||
description: parsed.data.description ?? null,
|
||||
priority: parsed.data.priority,
|
||||
sort_order: (last?.sort_order ?? 0) + 1.0,
|
||||
sort_order: parseCodeNumber(code),
|
||||
status: 'TO_DO',
|
||||
},
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue