Productionize EventSphere platform

This commit is contained in:
Austin A
2026-04-25 21:02:19 +01:00
commit 1f1d30a9f5
171 changed files with 18682 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "../../prisma/prisma.service";
@Injectable()
export class CrmService {
constructor(private readonly prisma: PrismaService) {}
async summary(tenantId: string) {
const [leadCount, dealCount, wonDeals, openDeals, leadsByStatus] = await Promise.all([
this.prisma.cRMLead.count({ where: { tenantId } }),
this.prisma.cRMDeal.count({ where: { tenantId } }),
this.prisma.cRMDeal.aggregate({ where: { tenantId, stage: "closed_won" }, _sum: { valueKobo: true } }),
this.prisma.cRMDeal.aggregate({ where: { tenantId, stage: { notIn: ["closed_won", "closed_lost"] } }, _sum: { valueKobo: true } }),
this.prisma.cRMLead.groupBy({ by: ["status"], where: { tenantId }, _count: { _all: true } })
]);
return {
leadCount,
dealCount,
wonValueKobo: wonDeals._sum.valueKobo ?? 0,
openValueKobo: openDeals._sum.valueKobo ?? 0,
leadsByStatus: leadsByStatus.map((row) => ({ status: row.status, count: row._count._all }))
};
}
leads(tenantId: string) {
return this.prisma.cRMLead.findMany({
where: { tenantId },
orderBy: { createdAt: "desc" },
include: { event: true, attendee: true, deals: true }
});
}
createLead(tenantId: string, payload: any) {
return this.prisma.cRMLead.create({
data: {
tenantId,
eventId: payload.eventId || null,
attendeeId: payload.attendeeId || null,
fullName: String(payload.fullName ?? ""),
email: String(payload.email ?? "").toLowerCase().trim(),
phone: payload.phone ? String(payload.phone) : null,
source: payload.source ? String(payload.source) : "manual",
status: payload.status ? String(payload.status) : "new",
valueKobo: Number(payload.valueKobo ?? 0)
}
});
}
async updateLead(tenantId: string, id: string, payload: any) {
const row = await this.prisma.cRMLead.findFirst({ where: { tenantId, id } });
if (!row) throw new NotFoundException("Lead not found");
return this.prisma.cRMLead.update({
where: { id },
data: {
fullName: payload.fullName === undefined ? undefined : String(payload.fullName),
email: payload.email === undefined ? undefined : String(payload.email).toLowerCase().trim(),
phone: payload.phone === undefined ? undefined : payload.phone ? String(payload.phone) : null,
source: payload.source === undefined ? undefined : String(payload.source),
status: payload.status === undefined ? undefined : String(payload.status),
valueKobo: payload.valueKobo === undefined ? undefined : Number(payload.valueKobo)
}
});
}
async removeLead(tenantId: string, id: string) {
const row = await this.prisma.cRMLead.findFirst({ where: { tenantId, id } });
if (!row) throw new NotFoundException("Lead not found");
await this.prisma.cRMLead.delete({ where: { id } });
return { ok: true };
}
deals(tenantId: string) {
return this.prisma.cRMDeal.findMany({
where: { tenantId },
orderBy: { updatedAt: "desc" },
include: { lead: true, event: true, activities: true }
});
}
createDeal(tenantId: string, payload: any) {
return this.prisma.cRMDeal.create({
data: {
tenantId,
leadId: payload.leadId || null,
eventId: payload.eventId || null,
title: String(payload.title ?? ""),
stage: payload.stage ? String(payload.stage) : "new",
valueKobo: Number(payload.valueKobo ?? 0),
probability: Number(payload.probability ?? 0),
ownerUserId: payload.ownerUserId || null,
expectedCloseAt: payload.expectedCloseAt ? new Date(payload.expectedCloseAt) : null
}
});
}
async updateDeal(tenantId: string, id: string, payload: any) {
const row = await this.prisma.cRMDeal.findFirst({ where: { tenantId, id } });
if (!row) throw new NotFoundException("Deal not found");
return this.prisma.cRMDeal.update({
where: { id },
data: {
title: payload.title === undefined ? undefined : String(payload.title),
stage: payload.stage === undefined ? undefined : String(payload.stage),
valueKobo: payload.valueKobo === undefined ? undefined : Number(payload.valueKobo),
probability: payload.probability === undefined ? undefined : Number(payload.probability),
expectedCloseAt: payload.expectedCloseAt === undefined ? undefined : payload.expectedCloseAt ? new Date(payload.expectedCloseAt) : null
}
});
}
async removeDeal(tenantId: string, id: string) {
const row = await this.prisma.cRMDeal.findFirst({ where: { tenantId, id } });
if (!row) throw new NotFoundException("Deal not found");
await this.prisma.cRMDeal.delete({ where: { id } });
return { ok: true };
}
activities(tenantId: string) {
return this.prisma.cRMActivity.findMany({
where: { tenantId },
orderBy: { createdAt: "desc" },
include: { lead: true, deal: true }
});
}
createActivity(tenantId: string, userId: string, payload: any) {
return this.prisma.cRMActivity.create({
data: {
tenantId,
userId,
leadId: payload.leadId || null,
dealId: payload.dealId || null,
type: payload.type ? String(payload.type) : "note",
note: String(payload.note ?? ""),
dueAt: payload.dueAt ? new Date(payload.dueAt) : null,
completedAt: payload.completedAt ? new Date(payload.completedAt) : null
}
});
}
}