wicked-eggs-push/apps/server/routers/deals.ts
2026-05-13 12:18:03 +05:00

122 lines
3.6 KiB
TypeScript

import { z } from "zod";
import { router, publicProcedure } from "../trpc";
const DealsPhaseOptions = [
"New",
"Demo scheduled",
"Contract signed",
"Lost",
] as const;
// Create a Zod enum from the phase options
const phaseEnum = z.enum(DealsPhaseOptions);
export const dealsRouter = router({
getAll: publicProcedure.query(async ({ ctx }) => {
return await ctx.queryBuilder
.selectFrom("deals")
.select(["id", "name", "phase", "createdAt", "updatedAt"])
.with({
mainContact: qb => qb.select(["id", "firstName", "lastName", "mail"])
})
.orderBy("createdAt", "desc")
.execute();
}),
getById: publicProcedure
.input(z.object({ id: z.number() }))
.query(async ({ ctx, input }) => {
// Get the deal with all fields including links
const deal = await ctx.queryBuilder
.selectFrom("deals")
.select(["id", "name", "phase", "createdAt", "updatedAt"])
.with({ mainContact: qb => qb.select(["id", "firstName", "lastName", "mail"]) })
.where("id", "=", input.id)
.executeTakeFirst();
return deal;
}),
search: publicProcedure
.input(z.object({ query: z.string(), limit: z.number().optional() }))
.query(async ({ ctx, input }) => {
let query = ctx.queryBuilder
.selectFrom("deals")
.select(["id", "name", "phase"]);
if (input.query.trim()) {
query = query.where("name", "contains", input.query);
}
return await query
.orderBy("name", "asc")
.execute();
}),
// Get detailed deal info with contact and company for details page
getDetails: publicProcedure
.input(z.object({ id: z.number() }))
.query(async ({ ctx, input }) => {
// Get the deal with contact info
const deal = await ctx.queryBuilder
.selectFrom("deals")
.select(["id", "name", "phase", "lostReason", "createdAt", "updatedAt"])
.with({
mainContact: qb => qb.select(["id", "firstName", "lastName", "mail", "phone1", "phone2", "notes"])
.with({ company: companyQb => companyQb.select(["id", "name", "website", "notes"]) })
})
.with({
activities: qb => qb.select(["id", "name", "type", "plannedAt", "doneAt", "createdAt"])
.with({ notes: notesQb => notesQb.select(["id", "name", "createdAt"]) })
})
.where("id", "=", input.id)
.executeTakeFirst();
return deal;
}),
create: publicProcedure
.input(
z.object({
name: z.string().min(1),
phase: phaseEnum.default("New"),
mainContact: z.array(z.number()).optional(),
})
)
.mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder
.insertInto("deals")
.values(input)
.executeTakeFirst();
}),
update: publicProcedure
.input(
z.object({
id: z.number(),
name: z.string().optional(),
phase: phaseEnum.optional(),
mainContact: z.array(z.number()).optional(),
})
)
.mutation(async ({ ctx, input }) => {
const { id, ...data } = input;
// Filter out undefined values to avoid overwriting with undefined
const updateData = Object.fromEntries(
Object.entries(data).filter(([, v]) => v !== undefined)
) as typeof data;
return await ctx.queryBuilder
.update("deals")
.set(updateData)
.where("id", "=", id)
.execute();
}),
delete: publicProcedure
.input(z.object({ id: z.number() }))
.mutation(async ({ ctx, input }) => {
return await ctx.queryBuilder
.deleteFrom("deals")
.where("id", "=", input.id)
.execute();
}),
});