fix: normalize GHL CRM sync statuses
This commit is contained in:
parent
7786336cfb
commit
9dfee33e49
1 changed files with 95 additions and 11 deletions
106
convex/crm.ts
106
convex/crm.ts
|
|
@ -147,6 +147,88 @@ function matchesSearch(values: Array<string | undefined>, search: string) {
|
||||||
return haystack.includes(search)
|
return haystack.includes(search)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeConversationStatus(value?: string) {
|
||||||
|
const normalized = String(value || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
|
||||||
|
if (!normalized) {
|
||||||
|
return "open"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
"closed",
|
||||||
|
"completed",
|
||||||
|
"complete",
|
||||||
|
"ended",
|
||||||
|
"resolved",
|
||||||
|
"done",
|
||||||
|
].includes(normalized)
|
||||||
|
) {
|
||||||
|
return "closed"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
[
|
||||||
|
"archived",
|
||||||
|
"spam",
|
||||||
|
"blocked",
|
||||||
|
"blacklisted",
|
||||||
|
"do_not_contact",
|
||||||
|
"dnd",
|
||||||
|
].includes(normalized)
|
||||||
|
) {
|
||||||
|
return "archived"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "open"
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeConversationDirection(value?: string) {
|
||||||
|
const normalized = String(value || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
|
||||||
|
if (normalized === "inbound") {
|
||||||
|
return "inbound"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (normalized === "outbound") {
|
||||||
|
return "outbound"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "mixed"
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeRecordingStatus(value?: string) {
|
||||||
|
const normalized = String(value || "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
|
||||||
|
if (!normalized) {
|
||||||
|
return "pending"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["completed", "complete", "ready", "available"].includes(normalized)) {
|
||||||
|
return "completed"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["starting", "queued"].includes(normalized)) {
|
||||||
|
return "starting"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["recording", "in_progress", "processing"].includes(normalized)) {
|
||||||
|
return "recording"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (["failed", "error"].includes(normalized)) {
|
||||||
|
return "failed"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "pending"
|
||||||
|
}
|
||||||
|
|
||||||
async function buildContactTimeline(ctx, contactId) {
|
async function buildContactTimeline(ctx, contactId) {
|
||||||
const conversations = await ctx.db
|
const conversations = await ctx.db
|
||||||
.query("conversations")
|
.query("conversations")
|
||||||
|
|
@ -449,8 +531,8 @@ export const importConversation = mutation({
|
||||||
channel:
|
channel:
|
||||||
payload.channel === "SMS" || payload.type === "sms" ? "sms" : "call",
|
payload.channel === "SMS" || payload.type === "sms" ? "sms" : "call",
|
||||||
source: `${args.provider}:mirror`,
|
source: `${args.provider}:mirror`,
|
||||||
status: payload.status || "open",
|
status: normalizeConversationStatus(payload.status),
|
||||||
direction: payload.direction || "mixed",
|
direction: normalizeConversationDirection(payload.direction),
|
||||||
startedAt:
|
startedAt:
|
||||||
typeof payload.dateAdded === "string"
|
typeof payload.dateAdded === "string"
|
||||||
? new Date(payload.dateAdded).getTime()
|
? new Date(payload.dateAdded).getTime()
|
||||||
|
|
@ -525,8 +607,8 @@ export const importMessage = mutation({
|
||||||
? "call"
|
? "call"
|
||||||
: "unknown",
|
: "unknown",
|
||||||
source: `${args.provider}:mirror`,
|
source: `${args.provider}:mirror`,
|
||||||
status: "open",
|
status: normalizeConversationStatus(payload.conversationStatus),
|
||||||
direction: payload.direction || "mixed",
|
direction: normalizeConversationDirection(payload.direction),
|
||||||
startedAt:
|
startedAt:
|
||||||
typeof payload.dateAdded === "string"
|
typeof payload.dateAdded === "string"
|
||||||
? new Date(payload.dateAdded).getTime()
|
? new Date(payload.dateAdded).getTime()
|
||||||
|
|
@ -605,7 +687,7 @@ export const importRecording = mutation({
|
||||||
channel: "call",
|
channel: "call",
|
||||||
source: `${args.provider}:mirror`,
|
source: `${args.provider}:mirror`,
|
||||||
status: "closed",
|
status: "closed",
|
||||||
direction: payload.direction || "mixed",
|
direction: normalizeConversationDirection(payload.direction),
|
||||||
startedAt:
|
startedAt:
|
||||||
typeof payload.createdAt === "string"
|
typeof payload.createdAt === "string"
|
||||||
? new Date(payload.createdAt).getTime()
|
? new Date(payload.createdAt).getTime()
|
||||||
|
|
@ -624,7 +706,7 @@ export const importRecording = mutation({
|
||||||
source: `${args.provider}:mirror`,
|
source: `${args.provider}:mirror`,
|
||||||
recordingId: payload.recordingId || payload.id || args.entityId,
|
recordingId: payload.recordingId || payload.id || args.entityId,
|
||||||
recordingUrl: payload.recordingUrl,
|
recordingUrl: payload.recordingUrl,
|
||||||
recordingStatus: payload.recordingStatus || "completed",
|
recordingStatus: normalizeRecordingStatus(payload.recordingStatus),
|
||||||
transcriptionText: payload.transcript,
|
transcriptionText: payload.transcript,
|
||||||
durationMs:
|
durationMs:
|
||||||
typeof payload.durationMs === "number"
|
typeof payload.durationMs === "number"
|
||||||
|
|
@ -883,7 +965,7 @@ export const runGhlMirror = action({
|
||||||
entityId: "contacts",
|
entityId: "contacts",
|
||||||
cursor: contactsCursor,
|
cursor: contactsCursor,
|
||||||
status: "synced",
|
status: "synced",
|
||||||
error: undefined,
|
error: "",
|
||||||
metadata: JSON.stringify({
|
metadata: JSON.stringify({
|
||||||
imported: summary.contacts,
|
imported: summary.contacts,
|
||||||
pages: contactsPages,
|
pages: contactsPages,
|
||||||
|
|
@ -958,7 +1040,7 @@ export const runGhlMirror = action({
|
||||||
entityId: "conversations",
|
entityId: "conversations",
|
||||||
cursor: conversationCursors.Call || conversationCursors.SMS,
|
cursor: conversationCursors.Call || conversationCursors.SMS,
|
||||||
status: "synced",
|
status: "synced",
|
||||||
error: undefined,
|
error: "",
|
||||||
metadata: JSON.stringify({
|
metadata: JSON.stringify({
|
||||||
imported: summary.conversations,
|
imported: summary.conversations,
|
||||||
cursors: conversationCursors,
|
cursors: conversationCursors,
|
||||||
|
|
@ -1021,7 +1103,7 @@ export const runGhlMirror = action({
|
||||||
entityId: "messages",
|
entityId: "messages",
|
||||||
cursor: messageCursors.Call || messageCursors.SMS,
|
cursor: messageCursors.Call || messageCursors.SMS,
|
||||||
status: "synced",
|
status: "synced",
|
||||||
error: undefined,
|
error: "",
|
||||||
metadata: JSON.stringify({
|
metadata: JSON.stringify({
|
||||||
imported: summary.messages,
|
imported: summary.messages,
|
||||||
cursors: messageCursors,
|
cursors: messageCursors,
|
||||||
|
|
@ -1082,7 +1164,7 @@ export const runGhlMirror = action({
|
||||||
entityId: "recordings",
|
entityId: "recordings",
|
||||||
cursor: String(nextPage),
|
cursor: String(nextPage),
|
||||||
status: "synced",
|
status: "synced",
|
||||||
error: undefined,
|
error: "",
|
||||||
metadata: JSON.stringify({
|
metadata: JSON.stringify({
|
||||||
imported: summary.recordings,
|
imported: summary.recordings,
|
||||||
nextPage,
|
nextPage,
|
||||||
|
|
@ -1107,7 +1189,9 @@ export const runGhlMirror = action({
|
||||||
entityType: "reconcile",
|
entityType: "reconcile",
|
||||||
entityId: "reconcile",
|
entityId: "reconcile",
|
||||||
status: reconcile.mismatches?.length ? "mismatch" : "reconciled",
|
status: reconcile.mismatches?.length ? "mismatch" : "reconciled",
|
||||||
error: undefined,
|
error: reconcile.mismatches?.length
|
||||||
|
? "Some mirrored records are missing locally."
|
||||||
|
: "",
|
||||||
metadata: JSON.stringify({
|
metadata: JSON.stringify({
|
||||||
checked: reconcile.checked,
|
checked: reconcile.checked,
|
||||||
mismatches: reconcile.mismatches || [],
|
mismatches: reconcile.mismatches || [],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue