feat(ST-352): add claimStoryAction, unclaimStoryAction, reassignStoryAction, claimAllUnassignedInActiveSprintAction
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ef5c7c7675
commit
4ae4edb033
1 changed files with 90 additions and 0 deletions
|
|
@ -7,6 +7,7 @@ import { z } from 'zod'
|
|||
import { prisma } from '@/lib/prisma'
|
||||
import { SessionData, sessionOptions } from '@/lib/session'
|
||||
import { getAccessibleProduct, productAccessFilter } from '@/lib/product-access'
|
||||
import { requireProductWriter } from '@/lib/auth'
|
||||
|
||||
async function getSession() {
|
||||
return getIronSession<SessionData>(await cookies(), sessionOptions)
|
||||
|
|
@ -208,6 +209,95 @@ export async function getStoryLogsAction(storyId: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function claimStoryAction(storyId: string, productId: string) {
|
||||
try {
|
||||
await requireProductWriter(productId)
|
||||
} catch (e: unknown) {
|
||||
return { error: e instanceof Error ? e.message : 'Geen toegang' }
|
||||
}
|
||||
|
||||
const session = await getSession()
|
||||
const story = await prisma.story.findFirst({ where: { id: storyId, product_id: productId } })
|
||||
if (!story) return { error: 'Story niet gevonden' }
|
||||
|
||||
await prisma.story.update({ where: { id: storyId }, data: { assignee_id: session.userId } })
|
||||
|
||||
revalidatePath(`/products/${productId}/sprint`)
|
||||
revalidatePath('/solo')
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
export async function unclaimStoryAction(storyId: string, productId: string) {
|
||||
try {
|
||||
await requireProductWriter(productId)
|
||||
} catch (e: unknown) {
|
||||
return { error: e instanceof Error ? e.message : 'Geen toegang' }
|
||||
}
|
||||
|
||||
const story = await prisma.story.findFirst({ where: { id: storyId, product_id: productId } })
|
||||
if (!story) return { error: 'Story niet gevonden' }
|
||||
|
||||
await prisma.story.update({ where: { id: storyId }, data: { assignee_id: null } })
|
||||
|
||||
revalidatePath(`/products/${productId}/sprint`)
|
||||
revalidatePath('/solo')
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
export async function reassignStoryAction(storyId: string, productId: string, targetUserId: string) {
|
||||
try {
|
||||
await requireProductWriter(productId)
|
||||
} catch (e: unknown) {
|
||||
return { error: e instanceof Error ? e.message : 'Geen toegang' }
|
||||
}
|
||||
|
||||
// Validate target user is owner or member of the product
|
||||
const product = await prisma.product.findFirst({
|
||||
where: {
|
||||
id: productId,
|
||||
OR: [
|
||||
{ user_id: targetUserId },
|
||||
{ members: { some: { user_id: targetUserId } } },
|
||||
],
|
||||
},
|
||||
})
|
||||
if (!product) return { error: 'Gebruiker is geen lid van dit product' }
|
||||
|
||||
const story = await prisma.story.findFirst({ where: { id: storyId, product_id: productId } })
|
||||
if (!story) return { error: 'Story niet gevonden' }
|
||||
|
||||
await prisma.story.update({ where: { id: storyId }, data: { assignee_id: targetUserId } })
|
||||
|
||||
revalidatePath(`/products/${productId}/sprint`)
|
||||
revalidatePath('/solo')
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
export async function claimAllUnassignedInActiveSprintAction(productId: string) {
|
||||
try {
|
||||
await requireProductWriter(productId)
|
||||
} catch (e: unknown) {
|
||||
return { error: e instanceof Error ? e.message : 'Geen toegang' }
|
||||
}
|
||||
|
||||
const session = await getSession()
|
||||
const userId = session.userId
|
||||
|
||||
const sprint = await prisma.sprint.findFirst({
|
||||
where: { product_id: productId, status: 'ACTIVE' },
|
||||
})
|
||||
if (!sprint) return { error: 'Geen actieve sprint gevonden' }
|
||||
|
||||
const result = await prisma.story.updateMany({
|
||||
where: { sprint_id: sprint.id, product_id: productId, assignee_id: null },
|
||||
data: { assignee_id: userId },
|
||||
})
|
||||
|
||||
revalidatePath(`/products/${productId}/sprint`)
|
||||
revalidatePath('/solo')
|
||||
return { success: true, count: result.count }
|
||||
}
|
||||
|
||||
export async function reorderStoriesAction(
|
||||
pbiId: string,
|
||||
productId: string,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue