Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM, PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source files covering clients, berths, interests/pipeline, documents/EOI, expenses/invoices, email, notifications, dashboard, admin, and client portal. CI/CD via Gitea Actions with Docker builds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
73
src/lib/services/email-draft.service.ts
Normal file
73
src/lib/services/email-draft.service.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { getQueue } from '@/lib/queue';
|
||||
|
||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface DraftRequest {
|
||||
interestId: string;
|
||||
clientId: string;
|
||||
portId: string;
|
||||
context: 'follow_up' | 'introduction' | 'stage_update' | 'general';
|
||||
additionalInstructions?: string;
|
||||
}
|
||||
|
||||
export interface DraftResult {
|
||||
subject: string;
|
||||
body: string;
|
||||
generatedAt: Date;
|
||||
}
|
||||
|
||||
// ─── Request draft (enqueues job) ─────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Request an AI-generated email draft.
|
||||
* Enqueues a job on the 'ai' queue. Returns jobId for polling.
|
||||
* Job payload contains ONLY entity IDs (no PII).
|
||||
*/
|
||||
export async function requestEmailDraft(
|
||||
userId: string,
|
||||
request: DraftRequest,
|
||||
): Promise<{ jobId: string }> {
|
||||
const aiQueue = getQueue('ai');
|
||||
|
||||
const job = await aiQueue.add('generate-email-draft', {
|
||||
// No PII — only IDs and context parameters
|
||||
interestId: request.interestId,
|
||||
clientId: request.clientId,
|
||||
portId: request.portId,
|
||||
context: request.context,
|
||||
additionalInstructions: request.additionalInstructions,
|
||||
requestedBy: userId,
|
||||
});
|
||||
|
||||
return { jobId: job.id! };
|
||||
}
|
||||
|
||||
// ─── Poll for result ──────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Get the result of an email draft generation job.
|
||||
* Returns null if still processing.
|
||||
*/
|
||||
export async function getEmailDraftResult(jobId: string): Promise<DraftResult | null> {
|
||||
const aiQueue = getQueue('ai');
|
||||
const job = await aiQueue.getJob(jobId);
|
||||
|
||||
if (!job) return null;
|
||||
|
||||
const state = await job.getState();
|
||||
|
||||
if (state !== 'completed') return null;
|
||||
|
||||
const returnValue = job.returnvalue as
|
||||
| { subject: string; body: string; generatedAt: string }
|
||||
| undefined
|
||||
| null;
|
||||
|
||||
if (!returnValue) return null;
|
||||
|
||||
return {
|
||||
subject: returnValue.subject,
|
||||
body: returnValue.body,
|
||||
generatedAt: new Date(returnValue.generatedAt),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user