feat(chat): add outbound delivery statuses to omnichat thread UI
This commit is contained in:
@@ -357,6 +357,48 @@ async function getDashboard(auth: AuthContext | null) {
|
||||
}),
|
||||
]);
|
||||
|
||||
let omniMessagesRaw: Array<{
|
||||
id: string;
|
||||
contactId: string;
|
||||
channel: string;
|
||||
direction: string;
|
||||
text: string;
|
||||
status: string;
|
||||
occurredAt: Date;
|
||||
updatedAt: Date;
|
||||
}> = [];
|
||||
|
||||
if (communicationsRaw.length) {
|
||||
const contactIds = [...new Set(communicationsRaw.map((row) => row.contactId))];
|
||||
const minOccurredAt = communicationsRaw[0]?.occurredAt ?? new Date();
|
||||
const maxOccurredAt = communicationsRaw[communicationsRaw.length - 1]?.occurredAt ?? new Date();
|
||||
const fromOccurredAt = new Date(minOccurredAt.getTime() - 5 * 60 * 1000);
|
||||
const toOccurredAt = new Date(maxOccurredAt.getTime() + 5 * 60 * 1000);
|
||||
|
||||
omniMessagesRaw = await prisma.omniMessage.findMany({
|
||||
where: {
|
||||
teamId: ctx.teamId,
|
||||
contactId: { in: contactIds },
|
||||
occurredAt: {
|
||||
gte: fromOccurredAt,
|
||||
lte: toOccurredAt,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
contactId: true,
|
||||
channel: true,
|
||||
direction: true,
|
||||
text: true,
|
||||
status: true,
|
||||
occurredAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
orderBy: [{ occurredAt: "asc" }, { updatedAt: "asc" }],
|
||||
take: 5000,
|
||||
});
|
||||
}
|
||||
|
||||
const channelsByContactId = new Map<string, Set<string>>();
|
||||
for (const item of communicationsRaw) {
|
||||
if (!channelsByContactId.has(item.contactId)) {
|
||||
@@ -377,6 +419,50 @@ async function getDashboard(auth: AuthContext | null) {
|
||||
description: c.note?.content ?? "",
|
||||
}));
|
||||
|
||||
const omniByKey = new Map<string, typeof omniMessagesRaw>();
|
||||
for (const row of omniMessagesRaw) {
|
||||
const key = [row.contactId, row.channel, row.direction, row.text.trim()].join("|");
|
||||
if (!omniByKey.has(key)) omniByKey.set(key, []);
|
||||
omniByKey.get(key)?.push(row);
|
||||
}
|
||||
const consumedOmniMessageIds = new Set<string>();
|
||||
|
||||
const resolveDeliveryStatus = (m: (typeof communicationsRaw)[number]) => {
|
||||
if (m.kind !== "MESSAGE") return null;
|
||||
const key = [m.contactId, m.channel, m.direction, m.content.trim()].join("|");
|
||||
const candidates = omniByKey.get(key) ?? [];
|
||||
if (!candidates.length) {
|
||||
if (m.direction === "OUT" && m.channel === "TELEGRAM") return "PENDING";
|
||||
return null;
|
||||
}
|
||||
|
||||
const targetMs = m.occurredAt.getTime();
|
||||
let best: (typeof candidates)[number] | null = null;
|
||||
let bestDiff = Number.POSITIVE_INFINITY;
|
||||
|
||||
for (const candidate of candidates) {
|
||||
if (consumedOmniMessageIds.has(candidate.id)) continue;
|
||||
const diff = Math.abs(candidate.occurredAt.getTime() - targetMs);
|
||||
if (diff > 5 * 60 * 1000) continue;
|
||||
if (diff < bestDiff) {
|
||||
best = candidate;
|
||||
bestDiff = diff;
|
||||
continue;
|
||||
}
|
||||
if (diff === bestDiff && best && candidate.updatedAt.getTime() > best.updatedAt.getTime()) {
|
||||
best = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
if (!best) {
|
||||
if (m.direction === "OUT" && m.channel === "TELEGRAM") return "PENDING";
|
||||
return null;
|
||||
}
|
||||
|
||||
consumedOmniMessageIds.add(best.id);
|
||||
return best.status;
|
||||
};
|
||||
|
||||
const communications = communicationsRaw.map((m) => ({
|
||||
id: m.id,
|
||||
at: m.occurredAt.toISOString(),
|
||||
@@ -389,6 +475,7 @@ async function getDashboard(auth: AuthContext | null) {
|
||||
audioUrl: "",
|
||||
duration: m.durationSec ? new Date(m.durationSec * 1000).toISOString().slice(14, 19) : "",
|
||||
transcript: Array.isArray(m.transcriptJson) ? ((m.transcriptJson as any) as string[]) : [],
|
||||
deliveryStatus: resolveDeliveryStatus(m),
|
||||
}));
|
||||
|
||||
const calendar = calendarRaw.map((e) => ({
|
||||
@@ -1204,6 +1291,7 @@ export const crmGraphqlSchema = buildSchema(`
|
||||
audioUrl: String!
|
||||
duration: String!
|
||||
transcript: [String!]!
|
||||
deliveryStatus: String
|
||||
}
|
||||
|
||||
type CalendarEvent {
|
||||
|
||||
Reference in New Issue
Block a user