import { Socket } from "socket.io"; import { attachSocketEventPermissions } from "../middleware/authorization"; import { opportunities } from "../../../managers/opportunities"; const LIVE_QUOTE_PREVIEW_PERMISSION = "sales.opportunity.fetch"; export const registerLiveQuotePreviewHandlers = (socket: Socket) => { attachSocketEventPermissions(socket, { "opp:live_quote_preview": [LIVE_QUOTE_PREVIEW_PERMISSION], }); const registeredLivePreviewEvents = new Set(); socket.on( "opp:live_quote_preview", async ( payload: { id?: string | number }, ack?: (response: { ok: boolean; event?: string; error?: string }) => void, ) => { const oppId = payload?.id; const normalizedId = typeof oppId === "string" || typeof oppId === "number" ? `${oppId}` : ""; if (!normalizedId) { if (ack) return ack({ ok: false, error: "Missing opportunity id" }); socket.emit("opp:live_quote_preview:error", { message: "Missing opportunity id", }); return; } const dataEvent = `opp:live_quote_preview:${normalizedId}:data`; const previewEvent = `opp:live_quote_preview:${normalizedId}:preview`; const roomName = `opp:live_quote_preview:${normalizedId}`; if (!registeredLivePreviewEvents.has(dataEvent)) { registeredLivePreviewEvents.add(dataEvent); socket.join(roomName); socket.on(dataEvent, async (data: any) => { socket.to(roomName).emit(dataEvent, data); try { const opportunity = await opportunities.fetchRecord(normalizedId); const opts = data?.options && typeof data.options === "object" ? data.options : data; const previewBuffer = await opportunity.generateQuote({ lineItemPricing: opts?.lineItemPricing, includeQuoteNarrative: opts?.includeQuoteNarrative, includeItemNarratives: opts?.includeItemNarratives, logoPath: opts?.logoPath, showPreview: true, }); const previewBase64 = Buffer.from(previewBuffer).toString("base64"); socket.to(roomName).emit(previewEvent, { id: normalizedId, mimeType: "application/pdf", contentBase64: previewBase64, }); socket.to(roomName).emit(dataEvent, { id: normalizedId, mimeType: "application/pdf", contentBase64: previewBase64, }); socket.emit(previewEvent, { id: normalizedId, mimeType: "application/pdf", contentBase64: previewBase64, }); socket.emit(dataEvent, { id: normalizedId, mimeType: "application/pdf", contentBase64: previewBase64, }); } catch (err: any) { socket.emit("opp:live_quote_preview:error", { message: err?.message ?? "Failed to generate live quote preview", id: normalizedId, }); } }); } if (ack) return ack({ ok: true, event: dataEvent }); socket.emit("opp:live_quote_preview:ready", { id: normalizedId, event: dataEvent, }); }, ); };