feat: Complete code integration of modules (#18)
The complete code integration is done. Co-authored-by: Sagnik <sagnik7896@gmail.com> Reviewed-on: #18
This commit was merged in pull request #18.
This commit is contained in:
122
app/src/lib/crmMappers.ts
Normal file
122
app/src/lib/crmMappers.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
import type { ChatLogRecord, LeadRecord } from '@/lib/api';
|
||||
import type { Lead } from '@/types';
|
||||
import type { LeadBadge, LeadTag, LeadSource, Message, MessageSender, PipelineStage, SentimentLog } from '@/types/crm';
|
||||
|
||||
const TAG_MAP: Record<string, LeadTag> = {
|
||||
whale: '#CashBuyer',
|
||||
potential: '#Investor',
|
||||
hot: '#EndUser',
|
||||
};
|
||||
|
||||
export function mapLeadRecordToStoreLead(record: LeadRecord): Lead {
|
||||
const qualification = record.qualification.toLowerCase() as Lead['qualification'];
|
||||
const status = record.stage === 'closed'
|
||||
? 'closed'
|
||||
: record.stage === 'qualified' || record.stage === 'negotiation'
|
||||
? 'qualified'
|
||||
: record.score >= 75
|
||||
? 'hot'
|
||||
: record.stage === 'new'
|
||||
? 'new'
|
||||
: 'engaged';
|
||||
const tags = Array.isArray(record.metadata?.tags) ? (record.metadata.tags as string[]) : [];
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
phone: record.phone ?? '',
|
||||
source: mapSource(record.source),
|
||||
status,
|
||||
lastMessage: record.notes || 'No conversation summary yet.',
|
||||
lastActive: new Date(record.updated_at ?? record.created_at ?? Date.now()),
|
||||
unreadCount: 0,
|
||||
qualification: qualification === 'tire_kicker' || qualification === 'potential' || qualification === 'whale'
|
||||
? qualification
|
||||
: 'potential',
|
||||
budget: record.budget,
|
||||
interest: record.unit_interest,
|
||||
quantumDynamicsScore: record.score,
|
||||
tags: tags.length > 0 ? tags : [record.qualification],
|
||||
};
|
||||
}
|
||||
|
||||
export function mapLeadRecordToOracleLead(record: LeadRecord, chatLogs: ChatLogRecord[]): import('@/types/crm').Lead {
|
||||
const badge = mapBadge(record.qualification);
|
||||
const tags = mapOracleTags(record.qualification, record.metadata);
|
||||
return {
|
||||
id: record.id,
|
||||
name: record.name,
|
||||
phone: record.phone ?? '',
|
||||
stage: mapPipelineStage(record.stage),
|
||||
oracleScore: record.score,
|
||||
badge,
|
||||
tags,
|
||||
source: mapSource(record.source),
|
||||
budget: record.budget,
|
||||
unitInterest: record.unit_interest,
|
||||
profileImageUrl: `https://api.dicebear.com/9.x/glass/svg?seed=${encodeURIComponent(record.name)}`,
|
||||
visitedShowroom: record.stage === 'site_visit' || record.stage === 'negotiation' || record.stage === 'closed',
|
||||
inShowroomNow: record.stage === 'site_visit',
|
||||
messages: chatLogs.map(mapChatLogToOracleMessage),
|
||||
sentimentLog: buildSentimentLog(record.score, record.stage),
|
||||
};
|
||||
}
|
||||
|
||||
function mapSource(source: string): LeadSource {
|
||||
if (source === 'walkin' || source === 'website' || source === 'whatsapp') return source;
|
||||
return 'website';
|
||||
}
|
||||
|
||||
function mapPipelineStage(stage: string): PipelineStage {
|
||||
const normalized = stage.toLowerCase();
|
||||
if (normalized === 'new' || normalized === 'new_inquiries') return 'new_inquiries';
|
||||
if (normalized === 'qualified' || normalized === 'qualifying') return 'qualified';
|
||||
if (normalized === 'site_visit') return 'site_visit';
|
||||
if (normalized === 'negotiation') return 'negotiation';
|
||||
return 'closed';
|
||||
}
|
||||
|
||||
function mapBadge(qualification: string): LeadBadge | undefined {
|
||||
const normalized = qualification.toLowerCase();
|
||||
if (normalized === 'whale') return 'whale';
|
||||
if (normalized === 'hot' || normalized === 'potential') return 'hot';
|
||||
if (normalized === 'tire_kicker') return 'tire_kicker';
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function mapOracleTags(qualification: string, metadata: Record<string, unknown>): LeadTag[] {
|
||||
const mapped = TAG_MAP[qualification.toLowerCase()];
|
||||
const rawTags = Array.isArray(metadata?.tags) ? metadata.tags as string[] : [];
|
||||
const canonical = rawTags.includes('#CashBuyer') || mapped === '#CashBuyer'
|
||||
? '#CashBuyer'
|
||||
: rawTags.includes('#EndUser') || mapped === '#EndUser'
|
||||
? '#EndUser'
|
||||
: '#Investor';
|
||||
return [canonical];
|
||||
}
|
||||
|
||||
function mapChatLogToOracleMessage(log: ChatLogRecord): Message {
|
||||
return {
|
||||
id: log.id,
|
||||
sender: mapSender(log.sender),
|
||||
content: log.content,
|
||||
createdAt: log.created_at ?? new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
function mapSender(sender: string): MessageSender {
|
||||
if (sender === 'lead' || sender === 'oracle' || sender === 'system') return sender;
|
||||
return 'system';
|
||||
}
|
||||
|
||||
function buildSentimentLog(score: number, stage: string): SentimentLog[] {
|
||||
const base = Math.max(20, score - 18);
|
||||
const labels = stage === 'site_visit'
|
||||
? ['Entry', 'Showroom peak', 'Pricing review']
|
||||
: ['Discovery', 'Qualification', 'Follow-up'];
|
||||
return labels.map((label, index) => ({
|
||||
id: `${stage}-${index}`,
|
||||
at: `${10 + index}:0${index}`,
|
||||
score: Math.min(100, base + index * 9),
|
||||
note: label,
|
||||
}));
|
||||
}
|
||||
Reference in New Issue
Block a user