feat(11-04): add phase tRPC procedures
- Add requirePhaseRepository helper function - Add createPhase mutation with auto-numbering - Add listPhases query by initiative ID - Add getPhase query with NOT_FOUND error handling - Add updatePhase mutation procedure - Add createPhasesFromBreakdown bulk create mutation
This commit is contained in:
@@ -735,6 +735,107 @@ export const appRouter = router({
|
||||
const { id, ...data } = input;
|
||||
return repo.update(id, data);
|
||||
}),
|
||||
|
||||
// ===========================================================================
|
||||
// Phase Procedures
|
||||
// ===========================================================================
|
||||
|
||||
/**
|
||||
* Create a new phase for an initiative.
|
||||
* Auto-assigns the next phase number.
|
||||
*/
|
||||
createPhase: publicProcedure
|
||||
.input(z.object({
|
||||
initiativeId: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
}))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const repo = requirePhaseRepository(ctx);
|
||||
const nextNumber = await repo.getNextNumber(input.initiativeId);
|
||||
return repo.create({
|
||||
initiativeId: input.initiativeId,
|
||||
number: nextNumber,
|
||||
name: input.name,
|
||||
description: input.description ?? null,
|
||||
status: 'pending',
|
||||
});
|
||||
}),
|
||||
|
||||
/**
|
||||
* List phases for an initiative.
|
||||
* Returns phases ordered by number.
|
||||
*/
|
||||
listPhases: publicProcedure
|
||||
.input(z.object({ initiativeId: z.string().min(1) }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const repo = requirePhaseRepository(ctx);
|
||||
return repo.findByInitiativeId(input.initiativeId);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Get a phase by ID.
|
||||
* Throws NOT_FOUND if phase doesn't exist.
|
||||
*/
|
||||
getPhase: publicProcedure
|
||||
.input(z.object({ id: z.string().min(1) }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
const repo = requirePhaseRepository(ctx);
|
||||
const phase = await repo.findById(input.id);
|
||||
if (!phase) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: `Phase '${input.id}' not found`,
|
||||
});
|
||||
}
|
||||
return phase;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Update a phase.
|
||||
* Returns the updated phase.
|
||||
*/
|
||||
updatePhase: publicProcedure
|
||||
.input(z.object({
|
||||
id: z.string().min(1),
|
||||
name: z.string().min(1).optional(),
|
||||
description: z.string().optional(),
|
||||
status: z.enum(['pending', 'in_progress', 'completed']).optional(),
|
||||
}))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const repo = requirePhaseRepository(ctx);
|
||||
const { id, ...data } = input;
|
||||
return repo.update(id, data);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Create multiple phases from Architect breakdown output.
|
||||
* Accepts pre-numbered phases and creates them in bulk.
|
||||
*/
|
||||
createPhasesFromBreakdown: publicProcedure
|
||||
.input(z.object({
|
||||
initiativeId: z.string().min(1),
|
||||
phases: z.array(z.object({
|
||||
number: z.number(),
|
||||
name: z.string().min(1),
|
||||
description: z.string(),
|
||||
})),
|
||||
}))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const repo = requirePhaseRepository(ctx);
|
||||
const created: Phase[] = [];
|
||||
for (const p of input.phases) {
|
||||
const phase = await repo.create({
|
||||
initiativeId: input.initiativeId,
|
||||
number: p.number,
|
||||
name: p.name,
|
||||
description: p.description,
|
||||
status: 'pending',
|
||||
});
|
||||
created.push(phase);
|
||||
}
|
||||
return created;
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user