feat(11-02): add findByNumber and getNextNumber to PhaseRepository

- Add findByNumber method to lookup phase by initiative and number
- Add getNextNumber method to get next available phase number
- Implement both methods in DrizzlePhaseRepository adapter
- Use drizzle-orm max() and and() for query construction
This commit is contained in:
Lukas May
2026-01-31 19:05:04 +01:00
parent 91e57c66eb
commit 67cfd4d201
2 changed files with 33 additions and 1 deletions

View File

@@ -4,7 +4,7 @@
* Implements PhaseRepository interface using Drizzle ORM.
*/
import { eq, asc } from 'drizzle-orm';
import { eq, asc, and, max } from 'drizzle-orm';
import { nanoid } from 'nanoid';
import type { DrizzleDatabase } from '../../index.js';
import { phases, type Phase } from '../../schema.js';
@@ -58,6 +58,26 @@ export class DrizzlePhaseRepository implements PhaseRepository {
.orderBy(asc(phases.number));
}
async findByNumber(initiativeId: string, number: number): Promise<Phase | null> {
const result = await this.db
.select()
.from(phases)
.where(and(eq(phases.initiativeId, initiativeId), eq(phases.number, number)))
.limit(1);
return result[0] ?? null;
}
async getNextNumber(initiativeId: string): Promise<number> {
const result = await this.db
.select({ maxNumber: max(phases.number) })
.from(phases)
.where(eq(phases.initiativeId, initiativeId));
const maxNumber = result[0]?.maxNumber ?? 0;
return maxNumber + 1;
}
async update(id: string, data: UpdatePhaseData): Promise<Phase> {
const existing = await this.findById(id);
if (!existing) {

View File

@@ -46,6 +46,18 @@ export interface PhaseRepository {
*/
findByInitiativeId(initiativeId: string): Promise<Phase[]>;
/**
* Find a phase by initiative and number.
* Returns null if not found.
*/
findByNumber(initiativeId: string, number: number): Promise<Phase | null>;
/**
* Get the next available phase number for an initiative.
* Returns MAX(number) + 1, or 1 if no phases exist.
*/
getNextNumber(initiativeId: string): Promise<number>;
/**
* Update a phase.
* Throws if phase not found.