import { ProjectStatus } from "@capakraken/shared"; import { TRPCError } from "@trpc/server"; import { z } from "zod"; import { controllerProcedure, planningReadProcedure } from "../trpc.js"; import { mapProjectDetail, mapProjectSummary, mapProjectSummaryDetail, readProjectByIdentifierDetailSnapshot, readProjectSummariesSnapshot, readProjectSummaryDetailsSnapshot, resolveProjectIdentifierSnapshot, } from "./project-read-shared.js"; export const projectIdentifierReadProcedures = { resolveByIdentifier: planningReadProcedure .input(z.object({ identifier: z.string() })) .query(async ({ ctx, input }) => { const select = { id: true, shortCode: true, name: true, status: true, responsiblePerson: true, startDate: true, endDate: true, } as const; let project = await ctx.db.project.findUnique({ where: { id: input.identifier }, select, }); if (!project) { project = await ctx.db.project.findUnique({ where: { shortCode: input.identifier }, select, }); } if (!project) { project = await ctx.db.project.findFirst({ where: { name: { equals: input.identifier, mode: "insensitive" } }, select, }); } if (!project) { project = await ctx.db.project.findFirst({ where: { name: { contains: input.identifier, mode: "insensitive" } }, select, }); } if (!project) { throw new TRPCError({ code: "NOT_FOUND", message: "Project not found" }); } return project; }), searchSummaries: planningReadProcedure .input(z.object({ search: z.string().optional(), status: z.nativeEnum(ProjectStatus).optional(), limit: z.number().int().min(1).max(50).default(20), })) .query(async ({ ctx, input }) => { const { items, exactMatch } = await readProjectSummariesSnapshot(ctx, input); const formatted = items.map(mapProjectSummary); if (items.length > 0 && input.search && !exactMatch) { return { suggestions: formatted, note: `No exact match for "${input.search}". These projects match some of the search terms:`, }; } return formatted; }), searchSummariesDetail: controllerProcedure .input(z.object({ search: z.string().optional(), status: z.nativeEnum(ProjectStatus).optional(), limit: z.number().int().min(1).max(50).default(20), })) .query(async ({ ctx, input }) => { const { items, exactMatch } = await readProjectSummaryDetailsSnapshot(ctx, input); const formatted = items.map(mapProjectSummaryDetail); if (items.length > 0 && input.search && !exactMatch) { return { suggestions: formatted, note: `No exact match for "${input.search}". These projects match some of the search terms:`, }; } return formatted; }), getByIdentifier: planningReadProcedure .input(z.object({ identifier: z.string() })) .query(async ({ ctx, input }) => resolveProjectIdentifierSnapshot(ctx, input.identifier)), getByIdentifierDetail: controllerProcedure .input(z.object({ identifier: z.string() })) .query(async ({ ctx, input }) => { const project = await readProjectByIdentifierDetailSnapshot(ctx, input.identifier); return mapProjectDetail(project, project.topAssignments); }), };