import { v } from "convex/values"; import { normalizeListLimit } from "./domain"; import { mutation, query } from "./_generated/server"; export const create = mutation({ args: { campaignId: v.optional(v.id("campaigns")), companyName: v.string(), niche: v.optional(v.string()), address: v.optional(v.string()), city: v.optional(v.string()), postalCode: v.optional(v.string()), googlePlaceId: v.optional(v.string()), googleMapsUrl: v.optional(v.string()), websiteDomain: v.optional(v.string()), phone: v.optional(v.string()), email: v.optional(v.string()), emailSource: v.optional(v.string()), contactPerson: v.optional(v.string()), priority: v.optional( v.union( v.literal("high"), v.literal("medium"), v.literal("low"), v.literal("defer"), ), ), contactStatus: v.optional( v.union( v.literal("new"), v.literal("missing_contact"), v.literal("audit_ready"), v.literal("outreach_ready"), v.literal("contacted"), v.literal("replied"), v.literal("do_not_contact"), ), ), notes: v.optional(v.string()), }, handler: async (ctx, args) => { const now = Date.now(); return await ctx.db.insert("leads", { ...args, priority: args.priority ?? "medium", contactStatus: args.contactStatus ?? "new", duplicateStatus: "unchecked", blacklistStatus: "clear", createdAt: now, updatedAt: now, }); }, }); export const get = query({ args: { id: v.id("leads") }, handler: async (ctx, args) => { return await ctx.db.get(args.id); }, }); export const list = query({ args: { campaignId: v.optional(v.id("campaigns")), contactStatus: v.optional( v.union( v.literal("new"), v.literal("missing_contact"), v.literal("audit_ready"), v.literal("outreach_ready"), v.literal("contacted"), v.literal("replied"), v.literal("do_not_contact"), ), ), limit: v.optional(v.number()), }, handler: async (ctx, args) => { const limit = normalizeListLimit(args.limit); if (args.campaignId) { const campaignId = args.campaignId; return await ctx.db .query("leads") .withIndex("by_campaignId", (q) => q.eq("campaignId", campaignId)) .order("desc") .take(limit); } if (args.contactStatus) { const contactStatus = args.contactStatus; return await ctx.db .query("leads") .withIndex("by_contactStatus", (q) => q.eq("contactStatus", contactStatus), ) .order("desc") .take(limit); } return await ctx.db.query("leads").order("desc").take(limit); }, }); export const listFunnel = query({ args: { limit: v.optional(v.number()), }, handler: async (ctx, args) => { const limit = normalizeListLimit(args.limit); const leads = await ctx.db.query("leads").order("desc").take(limit); return await Promise.all( leads.map(async (lead) => { const outreach = await ctx.db .query("outreachRecords") .withIndex("by_leadId", (q) => q.eq("leadId", lead._id)) .order("desc") .take(1); const latestOutreach = outreach[0] ?? null; return { id: lead._id, companyName: lead.companyName, niche: lead.niche ?? null, address: lead.address ?? null, city: lead.city ?? null, postalCode: lead.postalCode ?? null, priority: lead.priority, contactStatus: lead.contactStatus, blacklistStatus: lead.blacklistStatus, email: lead.email ?? null, phone: lead.phone ?? null, contactPerson: lead.contactPerson ?? null, websiteDomain: lead.websiteDomain ?? null, outreach: latestOutreach ? { approvalStatus: latestOutreach.approvalStatus, sendStatus: latestOutreach.sendStatus, responseStatus: latestOutreach.responseStatus, salesStatus: latestOutreach.salesStatus, } : null, }; }), ); }, });