import { BlueprintTarget, CreateBlueprintSchema, UpdateBlueprintSchema, type BlueprintFieldDefinition } from "@planarchy/shared"; import { TRPCError } from "@trpc/server"; import { z } from "zod"; import { findUniqueOrThrow } from "../db/helpers.js"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc.js"; export const blueprintRouter = createTRPCRouter({ list: protectedProcedure .input( z.object({ target: z.nativeEnum(BlueprintTarget).optional(), isActive: z.boolean().optional().default(true), }), ) .query(async ({ ctx, input }) => { return ctx.db.blueprint.findMany({ where: { ...(input.target ? { target: input.target } : {}), isActive: input.isActive, }, orderBy: { name: "asc" }, }); }), getById: protectedProcedure .input(z.object({ id: z.string() })) .query(async ({ ctx, input }) => { const blueprint = await findUniqueOrThrow( ctx.db.blueprint.findUnique({ where: { id: input.id } }), "Blueprint", ); return blueprint; }), create: adminProcedure .input(CreateBlueprintSchema) .mutation(async ({ ctx, input }) => { return ctx.db.blueprint.create({ data: { name: input.name, target: input.target, description: input.description, fieldDefs: input.fieldDefs as unknown as import("@planarchy/db").Prisma.InputJsonValue, defaults: input.defaults as unknown as import("@planarchy/db").Prisma.InputJsonValue, validationRules: input.validationRules as unknown as import("@planarchy/db").Prisma.InputJsonValue, } as unknown as Parameters[0]["data"], }); }), update: adminProcedure .input(z.object({ id: z.string(), data: UpdateBlueprintSchema })) .mutation(async ({ ctx, input }) => { await findUniqueOrThrow( ctx.db.blueprint.findUnique({ where: { id: input.id } }), "Blueprint", ); return ctx.db.blueprint.update({ where: { id: input.id }, data: { ...(input.data.name !== undefined ? { name: input.data.name } : {}), ...(input.data.description !== undefined ? { description: input.data.description } : {}), ...(input.data.fieldDefs !== undefined ? { fieldDefs: input.data.fieldDefs as unknown as import("@planarchy/db").Prisma.InputJsonValue } : {}), ...(input.data.defaults !== undefined ? { defaults: input.data.defaults as unknown as import("@planarchy/db").Prisma.InputJsonValue } : {}), ...(input.data.validationRules !== undefined ? { validationRules: input.data.validationRules as unknown as import("@planarchy/db").Prisma.InputJsonValue } : {}), } as unknown as Parameters[0]["data"], }); }), /** Dedicated mutation for saving role presets — separate from field defs to avoid Zod depth issues */ updateRolePresets: adminProcedure .input(z.object({ id: z.string(), rolePresets: z.array(z.unknown()) })) .mutation(async ({ ctx, input }) => { await findUniqueOrThrow( ctx.db.blueprint.findUnique({ where: { id: input.id } }), "Blueprint", ); return ctx.db.blueprint.update({ where: { id: input.id }, data: { rolePresets: input.rolePresets as unknown as import("@planarchy/db").Prisma.InputJsonValue }, }); }), delete: adminProcedure .input(z.object({ id: z.string() })) .mutation(async ({ ctx, input }) => { // Soft delete — mark as inactive return ctx.db.blueprint.update({ where: { id: input.id }, data: { isActive: false }, }); }), batchDelete: adminProcedure .input(z.object({ ids: z.array(z.string()).min(1).max(100) })) .mutation(async ({ ctx, input }) => { // Soft delete const updated = await ctx.db.$transaction( input.ids.map((id) => ctx.db.blueprint.update({ where: { id }, data: { isActive: false } }), ), ); return { count: updated.length }; }), getGlobalFieldDefs: protectedProcedure .input(z.object({ target: z.nativeEnum(BlueprintTarget) })) .query(async ({ ctx, input }) => { const blueprints = await ctx.db.blueprint.findMany({ where: { target: input.target, isGlobal: true, isActive: true }, select: { id: true, name: true, fieldDefs: true }, }); return blueprints.flatMap((b) => (b.fieldDefs as unknown as BlueprintFieldDefinition[]).map((f) => ({ ...f, blueprintId: b.id, blueprintName: b.name, })), ); }), setGlobal: adminProcedure .input(z.object({ id: z.string(), isGlobal: z.boolean() })) .mutation(async ({ ctx, input }) => { return ctx.db.blueprint.update({ where: { id: input.id }, data: { isGlobal: input.isGlobal }, }); }), });