feat: Add project filter to listInitiatives
Add findByProjectId to InitiativeRepository using a subquery on the initiative_projects junction table. Extend the listInitiatives tRPC procedure to accept an optional projectId filter that composes with the existing status filter. Add a project dropdown to the initiatives page alongside the status filter.
This commit is contained in:
@@ -4,10 +4,10 @@
|
||||
* Implements InitiativeRepository interface using Drizzle ORM.
|
||||
*/
|
||||
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
import { nanoid } from 'nanoid';
|
||||
import type { DrizzleDatabase } from '../../index.js';
|
||||
import { agents, initiatives, type Initiative } from '../../schema.js';
|
||||
import { agents, initiatives, initiativeProjects, type Initiative } from '../../schema.js';
|
||||
import type {
|
||||
InitiativeRepository,
|
||||
CreateInitiativeData,
|
||||
@@ -59,6 +59,18 @@ export class DrizzleInitiativeRepository implements InitiativeRepository {
|
||||
.where(eq(initiatives.status, status));
|
||||
}
|
||||
|
||||
async findByProjectId(projectId: string): Promise<Initiative[]> {
|
||||
const linkedIds = this.db
|
||||
.select({ id: initiativeProjects.initiativeId })
|
||||
.from(initiativeProjects)
|
||||
.where(eq(initiativeProjects.projectId, projectId));
|
||||
|
||||
return this.db
|
||||
.select()
|
||||
.from(initiatives)
|
||||
.where(inArray(initiatives.id, linkedIds));
|
||||
}
|
||||
|
||||
async update(id: string, data: UpdateInitiativeData): Promise<Initiative> {
|
||||
const [updated] = await this.db
|
||||
.update(initiatives)
|
||||
|
||||
@@ -57,6 +57,12 @@ export interface InitiativeRepository {
|
||||
*/
|
||||
update(id: string, data: UpdateInitiativeData): Promise<Initiative>;
|
||||
|
||||
/**
|
||||
* Find all initiatives linked to a specific project.
|
||||
* Returns empty array if none exist.
|
||||
*/
|
||||
findByProjectId(projectId: string): Promise<Initiative[]>;
|
||||
|
||||
/**
|
||||
* Delete an initiative.
|
||||
* Throws if initiative not found.
|
||||
|
||||
@@ -111,12 +111,22 @@ export function initiativeProcedures(publicProcedure: ProcedureBuilder) {
|
||||
listInitiatives: publicProcedure
|
||||
.input(z.object({
|
||||
status: z.enum(['active', 'completed', 'archived']).optional(),
|
||||
projectId: z.string().min(1).optional(),
|
||||
}).optional())
|
||||
.query(async ({ ctx, input }) => {
|
||||
const repo = requireInitiativeRepository(ctx);
|
||||
const initiatives = input?.status
|
||||
? await repo.findByStatus(input.status)
|
||||
: await repo.findAll();
|
||||
|
||||
let initiatives;
|
||||
if (input?.projectId) {
|
||||
const all = await repo.findByProjectId(input.projectId);
|
||||
initiatives = input.status
|
||||
? all.filter(i => i.status === input.status)
|
||||
: all;
|
||||
} else {
|
||||
initiatives = input?.status
|
||||
? await repo.findByStatus(input.status)
|
||||
: await repo.findAll();
|
||||
}
|
||||
|
||||
// Fetch active architect agents once for all initiatives
|
||||
const ARCHITECT_MODES = ['discuss', 'plan', 'detail', 'refine'];
|
||||
|
||||
Reference in New Issue
Block a user